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. Collapse | Copy Code
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 propertyUserName
,Email
andPassword
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 propertyEmail
andPassword
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 onConfirmPassword
to comparePassword
withConfirmPassword
.
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 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,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 implementsIClientValidatable
, an interface of ASP.NET MVC client validation. The IClientValidatable
has only one method GetClientValidationRule
that has the following signature: Collapse | Copy Code
IEnumerable<modelclientvalidationrule> GetClientValidationRules
(ModelMetadata metadata, ControllerContext context);
</modelclientvalidationrule>
ModelMetadata
is a container for common metadata. It allows classes to utilize model information when doing validationControllerContext
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 inSystem.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.
ValidatePasswordLengthAttribute
is a custom validation that inherits from ValidateAttribute
and implements IClientValidatable
. Collapse | Copy Code
[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 inModelState
to be used in action or view. Collapse | Copy Code
[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. Collapse | Copy Code
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.
Collapse | Copy Code
<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.
Collapse | Copy Code
<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
ofLogOnModel
.Collapse | Copy Code[Required] [Display(Name = "User name")] [Remote("DisallowName", "Account")] public string UserName { get; set; }
The first parameter isAction
name, the second parameter isController
name. - Create a
DisallowName
action inAccountController
.Collapse | Copy Codepublic 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:
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