Friday, 16 March 2012

Server Side Custom Annotation Validation in ASP.NET MVC 3

On this post we will see an example of server side custom data annotation validation in ASP.NET MVC 3. I will write the client side one on my next post.

Let's say we would like to create a custom validation using data annotation to check the sum of a list of comma separated integers. If the sum is equal to the sum parameter supplied then the value is valid otherwise an error message will be displayed.

To begin, we need to create a class that derived from ValidationAttribute. This class is used by other built in validation annotations. We also need to add reference to System.ComponentModel.DataAnnotations namespace in our class file.

When implementing ValidationAttribute class, we need to override its IsValid method (line 12 and 13 of the codes below). This method has two parameters; the first one is the value to be validated and the second one provides more information about the field (context) where the value comes from. This method is where we put our validation logic.

Because we want to pass the sum amount to be checked against as a parameter, in other words we would like to use the validation attribute as something like [SumIntegers(20)], then we need to create a constructor accepting the parameter (line 6-10).

On line 27, we return a hard coded error message when an exception has occurred. We could also have a custom error message when using the validation attribute. We do this through ErrorMessage property of the ValidationAttribute. Say we would like to use our validation attribute as something like [SumIntegers(20, ErrorMessage="custom error message for {0}")]; to do this we call FormatErrorMessage method, pass the context display name to the method, then return the message as ValidationResult type (line 21-22).

We also provide a default error message (line 7), passing it to the base constructor. If we do not specify a default error message and somehow we forget to provide one on the validation attribute then the error message returned will be 'The field [fieldname] is invalid.'

Notice also that on the first line there are some attributes for the validation class. This is the default one used if we do not specify any. So in this case we may not need to specify it. To know more about System.AttributeUsage you can see http://msdn.microsoft.com/en-us/library/tw5zxet9%28v=VS.100%29.aspx

[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class SumIntegers : ValidationAttribute
{
    private readonly int _sum;

    public SumIntegers(int sum)
        :base("The sum of integers of {0} are not equal to the one specified.")
    {
        _sum = sum;
    }

    protected override ValidationResult IsValid(
        object value, ValidationContext validationContext)
    {
        if (value != null)
        {
            try
            {
                if (value.ToString().Split(',').Select(n => int.Parse(n.Trim())).Sum() != _sum)
                {
                    var errorMessage = FormatErrorMessage(validationContext.DisplayName);
                    return new ValidationResult(errorMessage);
                }
            }
            catch (Exception ex)
            {
                return new ValidationResult("Input is not valid");
            }
        }

        return ValidationResult.Success;
    }
}

Finally, the usages:
[SumIntegers(50)]
public string CSVInput { get; set; }

[SumIntegers(20, ErrorMessage="custom error message for {0}")]
public string Numbers { get; set; }

No comments: