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.
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.
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.
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
.
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)
{
MembershipCreateStatus createStatus = MembershipService.CreateUser
(model.UserName, model.Password, model.Email);
if (createStatus == MembershipCreateStatus.Success)
{
FormsService.SignIn(model.UserName, false );
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("",
AccountValidation.ErrorCodeToString(createStatus));
}
}
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.
- 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.
- 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:
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