Wednesday, November 23, 2011

ASP.NET MVC3 Validation Basic

Introduction

The ASP.NET MVC3 comes with a validation feature that not only supports both server side and client side validation, but also hides all validation details to have a very clean controller code and HTML markup.

Validation Walkthrough

The easiest way to try ASP.NET MVC validation feature is to create a web application with the default internet application template that will automatically generate all essential validation code in it.
Validation Attribute on Model Class
Let’s take a look at the validation code generated for RegisterModel class.
public class RegisterModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.EmailAddress)]
    [Display(Name = "Email address")]
    public string Email { get; set; }

    [Required]
    [ValidatePasswordLength]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password 
  do not match.")]
    public string ConfirmPassword { get; set; }
}
  • The Required attribute is used on property UserNameEmail and Password to mark them as required.
  • The Display attribute is used on all properties to give them a display name as field label or in error message.
  • The DataType attribute is used on property Email and Password to indicate type of property.
  • The ValidationPasswordLength attribute is a custom validation attribute. I will talk more about it later.
  • The Compare attribute is used on ConfirmPassword to compare Password with ConfirmPassword.
Where is Validation Attribute from?
The general purpose validation attributes are defined in System.ComponentModel.DataAnnotations namespace (System.ComponentModel.DataAnnotations.dll). This includes Required attribute, Range attribute,RegularExpression attribute, StringLength attribute, etc. They all inherit from ValidationAttributebase class and override IsValid method to provide their specific validation logic. DisplayAttribute is also inSystem.ComponentMode.DataAnnotations namespace, but it’s a display attribute instead of validation attribute. DataTypeAttribute is a validation attribute, but is classified as display attribute in MSDN. FYI, inSystem.ComponentMode.DataAnnotations namespace, there are Data Modeling attributes,AssociationAttributeKeyAttribute, etc. designed for Entity Framework.
RequiredAttributeClass.gif
CompareAttribute is a special purpose validation attribute provided by ASP.NET MVC. It is in System.Web.Mvcnamespace (System.Web.Mvc.dll). Another validation attribute provided by ASP.NET MVC is RemoteAttribute that uses Ajax call to service side controller action to do validation. The CompareAttribute also implementsIClientValidatable, an interface of ASP.NET MVC client validation. The IClientValidatable has only one method GetClientValidationRule that has the following signature:
IEnumerable<modelclientvalidationrule> GetClientValidationRules
(ModelMetadata metadata, ControllerContext context);
</modelclientvalidationrule>
  • ModelMetadata is a container for common metadata. It allows classes to utilize model information when doing validation
  • ControllerContext is a container for HTTP request and other request environment data.
  • ModelClientValidationRule is a base class for client validation rule that is sent to the browser. There are six built-in validation rules in MVC: ModelClientValidationEqualToRule,ModelClientValidationRemoteRuleModelClientValidationRequiredRule,ModelClientValidationRangeRuleModelClientValidationStringLengthRule,ModelClientValidationRegexRule. If you pay attention, you can see all general purpose validation attributes in System.ComponentModel.DataAnnotations have a correspondingModelClientValidationRule in here. The ASP.NET MVC creates adapter, e.g.RequiredAttributeAdapter, to extend general purpose validation attribute to support ASP.NET MVC validation design.
CompareAttributeClass.gif
ValidatePasswordLengthAttribute is a custom validation that inherits from ValidateAttribute and implements IClientValidatable.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, 
  AllowMultiple = false, Inherited = true)]
    public sealed class ValidatePasswordLengthAttribute : ValidationAttribute, 
  IClientValidatable
    {
        private const string _defaultErrorMessage = "'{0}' 
   must be at least {1} characters long.";
        private readonly int _minCharacters = 
  Membership.Provider.MinRequiredPasswordLength;

        public ValidatePasswordLengthAttribute()
            : base(_defaultErrorMessage)
        {
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentCulture, ErrorMessageString,
                name, _minCharacters);
        }

        public override bool IsValid(object value)
        {
            string valueAsString = value as string;
            return (valueAsString != null && valueAsString.Length >= _minCharacters);
        }

        public IEnumerable<modelclientvalidationrule> 
 GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            return new[]{
                new ModelClientValidationStringLengthRule(FormatErrorMessage
  (metadata.GetDisplayName()), _minCharacters, int.MaxValue)
            };
        }
    }
</modelclientvalidationrule>
One note in here is the ValidatePasswordLengthAttribute has reference to Membership, so the application needs to have configuration of Membership provider in place to make it work.
ValidatePasswordLengthAttributeClass.gif
You can apply different validation attributes on a single property, this aggregate design makes ASP.NET MVC validation feature more powerful and flexible.
Server Side Validation
In order to see how our custom validation comes into play in ASP.NET MVC server side, let’s take a look at the call stack to IsValid method of custom validation attribute - ValidatePasswordLengthAttribute.
CallStack.gif
From the call stack, you can see the server side validating is happening during model binding step (DefaultModelBinder.BindModel(…)). The ModelValidator calls each validation attribute class to validate the model data based on a given setting. The validation results (ModelValidationResult) are stored inModelState to be used in action or view.
[HttpPost]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        MembershipCreateStatus createStatus = MembershipService.CreateUser
   (model.UserName, model.Password, model.Email);

        if (createStatus == MembershipCreateStatus.Success)
        {
            FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            ModelState.AddModelError("", 
  AccountValidation.ErrorCodeToString(createStatus));
        }
    }

    // If we got this far, something failed, redisplay form
    ViewBag.PasswordLength = MembershipService.MinPasswordLength;
    return View(model);
}
The ModelState.IsValid returns true or false from checking internal errors collection.
public bool IsValid
{
    get
    {
        return this.Values.All((ModelState modelState) => modelState.Errors.Count == 0);
    }
}
Client Side Validation
The client side validation is enabled by default in web.config.
<appSettings>
    <add key="ClientValidationEnabled" value="true"/> 
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
  </appSettings>
However, you must make sure jquery.validation.min.js and jquery.validation.unobtrusive.min.js are added in the view page also.
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript">
</script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" 
type="text/javascript"></script>
The extension methods for HtmlHelper class are required for validation: ValidateValidateFor,ValidationMessageValidationMessageForValidationSummary.
The jquery.validate.min.js is standard jQuery validation library. The jquery.validate.unobtrusive.min.js is an ASP.NET MVC client side validation library that is built on top of jQuery validation library. It uses HTML element attributes to store validation information. This design is very clean and intrusive to the UI designer.

Remote Validation Attribute in Action

The above validation code is generated from ASP.NET MVC project template. I also want to show Remote Validation Attribute here to show how to use it. The requirement is very simple - UserName “Bin” is not allow in the application, the application needs to show error right after entered “Bin” in User Name textbox.
  1. Add Remote attribute on UserName of LogOnModel.
    [Required]
    [Display(Name = "User name")]
    [Remote("DisallowName", "Account")]
    public string UserName { get; set; }
    The first parameter is Action name, the second parameter is Controller name.
  2. Create a DisallowName action in AccountController.
    public ActionResult DisallowName(string UserName)
    {
        if (UserName != "Bin")
        {
            return Json(true, JsonRequestBehavior.AllowGet);
        }
    
        return Json(string.Format("{0} is invalid", UserName), 
       JsonRequestBehavior.AllowGet);
    }
    That is, a remote validation is done. Let’s take a look at what it looks like on screen:
    LogOnScreen.gif

Conclusion

The validation design in ASP.NET MVC3 is very clean and powerful. It makes server and client side validation consistent.

Using the Code

The code is developed in Visual Studio 2010. There is no special requirement for using the code.

No comments:

Post a Comment