Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ASP.NET MVC3 Validation Basic

0.00/5 (No votes)
4 Sep 2011 2  
In this article, I will briefly explain how ASP.NET MVC3 validation works.

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 UserName, Email 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 ValidationAttribute base class and override IsValid method to provide their specific validation logic. DisplayAttribute is also in System.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, in System.ComponentMode.DataAnnotations namespace, there are Data Modeling attributes, AssociationAttribute, KeyAttribute, etc. designed for Entity Framework.

MVCValidationBasic/RequiredAttributeClass.gif

CompareAttribute is a special purpose validation attribute provided by ASP.NET MVC. It is in System.Web.Mvc namespace (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 implements IClientValidatable, 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, ModelClientValidationRemoteRule, ModelClientValidationRequiredRule, ModelClientValidationRangeRule, ModelClientValidationStringLengthRule, ModelClientValidationRegexRule. If you pay attention, you can see all general purpose validation attributes in System.ComponentModel.DataAnnotations have a corresponding ModelClientValidationRule in here. The ASP.NET MVC creates adapter, e.g. RequiredAttributeAdapter, to extend general purpose validation attribute to support ASP.NET MVC validation design.
MVCValidationBasic/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.

MVCValidationBasic/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.

MVCValidationBasic/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 in ModelState 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: Validate, ValidateFor, ValidationMessage, ValidationMessageFor, ValidationSummary.

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's it. A remote validation is done. Let’s take a look what looks like on screen:

    MVCValidationBasic/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.

History

  • 4th September, 2011: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here