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中有六种内置验证规则:
ModelClientValidationEqualToRule
ModelClientValidationRemoteRule
ModelClientValidationRequiredRule
ModelClientValidationRangeRule
ModelClientValidationStringLengthRule
ModelClientValidationRegexRule
如果仔细观察,会发现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
类的扩展方法进行验证:
Validate
ValidateFor
ValidationMessage
ValidationMessageFor
ValidationSummary
jquery.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中的验证设计非常干净且功能强大。它使得服务器端和客户端验证保持一致。