Monday, February 11, 2008

Extension method validators

I'm building an MVC Framework application at the moment and I wanted a simple, easy to grep way of validating fields passed back from forms. In MVC-land all the form variables are passed as parameters to the action method. Extension methods turned out to be a very neat solution for doing validation and data conversion. Here's an example:

[ControllerAction]
public void UpdateContact(
    int contactId,
    string name,
    string address1,
    string address2,
    string address3,
    string county,
    string postcode,
    string telephone,
    string email)
{
    try
    {
        name.Label("Name").IsRequired();
        address1.Label("Address 1").IsRequired();
        county.Label("County").IsRequired();
        postcode.Label("Postcode").IsRequired().IsPostcode();
        telephone.Label("Telephone").IsRequired().IsTelephoneNumber();
        email.Label("Email").IsRequired().IsEmail();
    }
    catch (ValidationException exception)
    {
        ContactViewData viewData = ContactViewData(contactId);
        viewData.ErrorMessage = exception.Message;
        RenderView("Contact", viewData);
        return;
    }

 // update the contact and render the view
}

As you can see we pass the contact's details from a form. In the try block the extension method validators are called. Any of them can raise a validation exception which is caught by the catch block and a view is rendered showing the validation error.

The 'Label' extension returns a ValidateField instance that can be consumed by any other validator, this is so that we can raise exceptions that can be displayed directly to the user:

public static ValidateField Label(this string value, string label)
{
    return new ValidateField { Value = value, Label = label };
}

The 'IsRequired' extension takes a ValidateField and checks that the value is not null or empty:

public static ValidateField IsRequired(this ValidateField validateField)
{
    if (string.IsNullOrEmpty(validateField.Value))
    {
        throw new ValidationException(string.Format("{0} is required", validateField.Label));
    }
    return validateField;
}

And finally the 'IsEmail' extension uses a Regex to validate the string value:

public static ValidateField IsEmail(this ValidateField validateField)
{
    // ignore is null or empty, use IsRequired in parrallel to check this if needed
    if (string.IsNullOrEmpty(validateField.Value)) return validateField;

    string patternLenient = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
    if (!Regex.Match(validateField.Value, patternLenient).Success)
    {
        throw new ValidationException(string.Format("{0} must be a valid email address", validateField.Label));
    }
    return validateField;
}

I'm finding extension methods a real boon for writing DSL-ish APIs. I'll leave the 'IsTelephone' and 'IsPostcode' exercises for your enjoyment :)

1 comment:

Anonymous said...

This finds only one invalid field..