ASP.NET MVC框架提供了强大的验证机制,它不仅支持服务器端和客户端验证,而且能够隐藏所有验证细节,使得控制器代码和HTML标记非常干净。本文将详细介绍ASP.NET MVC中的验证机制,包括如何使用模型类中的验证属性,以及服务器端和客户端验证的实现方式。
要体验ASP.NET MVC的验证特性,最简单的方法是创建一个使用默认的Internet应用程序模板的Web应用程序,它会自动生成所有必要的验证代码。
以RegisterModel类为例,可以看到生成的验证代码:
public class RegisterModel
{
[Required]
[Display(Name = "用户名")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "电子邮件地址")]
public string Email { get; set; }
[Required]
[ValidatePasswordLength]
[DataType(DataType.Password)]
[Display(Name = "密码")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "确认密码")]
[Compare("Password", ErrorMessage = "密码和确认密码不匹配。")]
public string ConfirmPassword { get; set; }
}
在上述代码中,Required属性用于标记UserName、Email和Password属性为必填项。Display属性用于为所有属性提供显示名称,作为字段标签或错误消息。DataType属性用于指示属性的类型。ValidatePasswordLength是一个自定义验证属性,稍后将详细介绍。Compare属性用于比较Password和ConfirmPassword。
通用验证属性定义在System.ComponentModel.DataAnnotations命名空间(System.ComponentModel.DataAnnotations.dll)中。这包括Required属性、Range属性、RegularExpression属性、StringLength属性等。它们都继承自ValidationAttribute基类,并重写IsValid方法以提供特定的验证逻辑。
DisplayAttribute也在System.ComponentModel.DataAnnotations命名空间中,但它是一个显示属性,而不是验证属性。DataTypeAttribute是一个验证属性,但在MSDN中被归类为显示属性。
在System.ComponentModel.DataAnnotations命名空间中,还有一些为Entity Framework设计的数据处理属性,如AssociationAttribute、KeyAttribute等。
CompareAttribute是ASP.NET MVC提供的专用验证属性,位于System.Web.Mvc命名空间(System.Web.Mvc.dll)。ASP.NET MVC还提供了另一个验证属性RemoteAttribute,它使用Ajax调用来执行服务器端控制器操作的验证。CompareAttribute还实现了IClientValidatable接口,这是ASP.NET MVC客户端验证的接口。IClientValidatable只有一个方法GetClientValidationRule,其签名如下:
IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context);
ModelMetadata是一个通用元数据的容器,它允许类在进行验证时使用模型信息。ControllerContext是一个包含HTTP请求和其他请求环境数据的容器。ModelClientValidationRule是客户端验证规则的基类,它被发送到浏览器。MVC中有六种内置验证规则:
ModelClientValidationEqualToRuleModelClientValidationRemoteRuleModelClientValidationRequiredRuleModelClientValidationRangeRuleModelClientValidationStringLengthRuleModelClientValidationRegexRule如果仔细观察,会发现System.ComponentModel.DataAnnotations中的所有通用验证属性在这里都有一个对应的ModelClientValidationRule。ASP.NET MVC创建了适配器,例如RequiredAttributeAdapter,以扩展通用验证属性以支持ASP.NET MVC验证设计。
ValidatePasswordLengthAttribute是一个自定义验证,继承自ValidationAttribute并实现了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)
};
}
}
需要注意的是,ValidatePasswordLengthAttribute引用了Membership,因此应用程序需要有Membership提供程序的配置才能使其工作。
为了看到自定义验证如何在ASP.NET MVC服务器端发挥作用,让来看一下调用自定义验证属性ValidatePasswordLengthAttribute的IsValid方法的调用栈。
从调用栈中,可以看到服务器端验证是在模型绑定步骤(DefaultModelBinder.BindModel(…))中进行的。ModelValidator调用每个验证属性类来验证模型数据,基于给定的设置。验证结果(ModelValidationResult)存储在ModelState中,以在操作或视图中使用。
[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);
}
ModelState.IsValid从检查内部错误集合返回true或false。
public bool IsValid
{
get
{
return this.Values.All((ModelState modelState) => modelState.Errors.Count == 0);
}
}
客户端验证在web.config中默认启用。
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
但是,必须确保在视图页面中也添加了jquery.validation.min.js和jquery.validate.unobtrusive.min.js。
<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>
需要HtmlHelper类的扩展方法进行验证:
ValidateValidateForValidationMessageValidationMessageForValidationSummaryjquery.validate.min.js是标准的jQuery验证库。jquery.validate.unobtrusive.min.js是ASP.NET MVC客户端验证库,它建立在jQuery验证库之上。它使用HTML元素属性来存储验证信息。这种设计非常干净,对UI设计师来说非常友好。
上面的验证代码是从ASP.NET MVC项目模板生成的。还想在这里展示Remote验证属性,以展示如何使用它。要求非常简单——应用程序不允许用户名为"Bin",应用程序需要在用户在用户名文本框中输入"Bin"后立即显示错误。
在LogOnModel的UserName上添加Remote属性。
[Required]
[Display(Name = "用户名")]
[Remote("DisallowName", "Account")]
public string UserName { get; set; }
第一个参数是Action名称,第二个参数是Controller名称。
在AccountController中创建一个DisallowName操作。
public ActionResult DisallowName(string UserName)
{
if (UserName != "Bin")
{
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json(string.Format("{0} is invalid", UserName), JsonRequestBehavior.AllowGet);
}
就是这样。远程验证完成了。让看看在屏幕上是什么样子的:
ASP.NET MVC3中的验证设计非常干净且功能强大。它使得服务器端和客户端验证保持一致。