[转载]ASP.NET MVC中的扩展点(九)验证

[转载]MVC中的扩展点(九)验证 – xfrog – 博客园.

验证用于保证用户输入的正确性,及时阻止用户提交错误数据,确保数据符合业务规则。

MVC中可直接在控制器Action方法中进行验证:检查传入参数,如果传入参数不符合业务规则,则通过控制器的ModelState属性的 AddModelError方法向模型状态添加错误消息,通过ModelState.IsValid判断模型是否全部通过验证,随后,在视图中通过 Html.ValidationSummary、Html.ValidationMessage、Html.ValidationMessageFor辅 助方法生成验证消息。

在Controller中验证并不是最好的方法:验证过于分散,容易造成重复代码,不利于维护与扩展。验证应该是和模型紧密相关的,如果我们将模型独立为 一个单独的程序集,那么验证也应该包含在模型的程序集中。从用户录入的数据到具体的模型,这个映射过程就是模型绑定,所以在绑定过程中实现验证是一个不错 的选择,这也是MVC中最重要的验证方式。

MVC默认绑定器(DefaultModelBinder)中包含了验证架构,具体来说,默认绑定器在将值提供器的数据填充到模型的时候,遵循以下流程: 调用默认绑定器的OnModelUpdating方法—>从值提供器获取值并填充到模型—>调用默认绑定器的OnModelUpdated方 法。DefaultModelBinder类中OnModelUpdating方法始终返回true,即默认绑定器在填充模型之前没有验证,在填充数据 后,OnModelUpdated中实现相应的验证。默认绑定器中验证体系类关系图如下:

Validator

模型绑定中的验证体系包含三个主要类:

验证器提供者:ModelValidatorProvider,用于产生验证器

验证器:ModelValidator,用于实现具体的验证逻辑,其中GetClientValidationRules用于返回客户端验证脚本使用的验证规则(ModelClientValidationRule),Validate方法用于实现服务端的验证逻辑。

特性:通常由提供者使用,根据特性指定的规则产生相应的验证器

ModelValidatorProviders是一个静态类,他包含一个用于保存MVC中默认验证器提供者的集合(静态属性Proviers),MVC 在填充完模型数据之后,依次对每一个模型属性,从Providers中获取所有的针对该属性的验证器,然后调用验证器上的Validate方法产生验证结 果(ModelValidationResult),绑定器根据该结果数据向ModelState中添加验证消息。

MVC中实现了三个默认的验证器提供者(相应产生三个验证器):

DataAnnotationsModelValidatorProvider: 用于实现.NET中的验证特性,即System.ComponentModel.DataAnnotations命名空间下的多种验证特性,包含用于限制 属性区间的RangeAttribute,用于验证数据是否符合正则表达式的RegularExpressionAttribute,用于指定必需项的 RequiredAttribute,用于限制字符串长度的 StringLengthAttribute,DataAnnotationsModelValidatorProvider通过桥接模式,将.NET的 验证特性转换为ModelValidator,所以我们可以直接在MVC中使用.NET验证特性来实现验证。

DataErrorInfoClassModelValidatorProvider: 此提供器主要是为了向后兼容,用于实现基于IDataErrorInfo接口的验证方式(MVC 1.0),即你可以为模型实现IDataErrorInfo接口,这样默认绑定器同样可以通过该接口来调用你的验证逻辑。

ClientDataTypeModelValidatorProvider: 此提供器只用于客户端对数值型数据的验证(产生相应的客户端验证脚本),他的Validate方法不会返回任何验证结果。

MVC中另外实现了一个抽象类:AssociatedValidatorProvider,它从ModelValidatorProvider继承,并重 写了GetValidator方法,增加传入了附加在模型属性上的特性集合。所以,如果我们需要实现基于特性的验证方式,则应该从此类继承实现自己的验证 器及相应的提供者类。当然我们也可以使用默认的DataAnnotationsModelValidatorProvider,这样我们只需要从 ValidationAttribute特性继承,并实现自己的验证逻辑。

客户端验证

ModelValidator中GetClientValidationRules方法可以返回用于客户端的验证规则,这些规则可以在客户端脚本中访问,客户端脚本根据这些验证规则检查用户录入资料,并将验证结果反馈给用户。

下例将实现一个ConfirmValidator,用于验证用户注册时两次密码必须输入一致:

1、创建一个空MVC项目

2、添加用户信息模型UserInfo.cs

显示行号 复制代码 UserInfo
  1. public class UserInfo
    
  2. {
    
  3.     public string UserName { get; set; }
    
  4.     public string Password { get; set; }
    
  5.     public string ConfirmPassword { get; set; }
    
  6.     public string Email { get; set; }
    
  7. }
    

.src_container { background-color: rgb(231, 229, 220); width: 99%; overflow: hidden; margin: 12px 0pt ! important; padding: 0px 3px 3px 0px; }.src_container .titlebar { background-color: rgb(212, 223, 255); border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(79, 129, 189) rgb(79, 129, 189) -moz-use-text-color; padding: 3px 24px; margin: 0pt; width: auto; line-height: 120%; overflow: hidden; text-align: left; font-size: 12px; }.src_container .toolbar { display: inline; font-weight: normal; font-size: 100%; float: right; color: rgb(0, 0, 255); text-align: left; overflow: hidden; }.toolbar span.button { display: inline; font-weight: normal; font-size: 100%; color: rgb(0, 0, 255); text-align: left; overflow: hidden; cursor: pointer; }.src_container div.clientarea { background-color: white; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; height: auto; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,courier,monospace,serif; }.src_container ol.mainarea { padding: 0pt 0pt 0pt 52px; margin: 0pt; background-color: rgb(247, 247, 255) ! important; }.number_show { padding-left: 52px ! important; list-style: decimal outside none ! important; }.number_show li { list-style: decimal outside none ! important; border-left: 1px dotted rgb(79, 129, 189); }.number_hide { padding-left: 0px ! important; list-style-type: none ! important; }.number_hide li { list-style-type: none ! important; border-left: 0px none; }ol.mainarea li { display: list-item ! important; font-size: 12px ! important; margin: 0pt ! important; line-height: 18px ! important; padding: 0pt 0pt 0pt 0px ! important; background-color: rgb(247, 247, 255) ! important; color: rgb(79, 129, 189); }ol.mainarea li pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap ol.mainarea li pre { white-space: pre-wrap; word-wrap: break-word; }ol.mainarea li pre.alt { background-color: rgb(247, 247, 255) ! important; }3、创建一个特性,用于指定与属性关联的另一个属性的名称

显示行号 复制代码 ConfirmValidatorAttribute
  1. public class ConfirmValidatorAttribute : Attribute
    
  2. {
    
  3.     public String ConfirmPropertyName { get; set; }
    
  4.     public ConfirmValidatorAttribute(string name)
    
  5.     {
    
  6.         ConfirmPropertyName = name;
    
  7.     }
    
  8. }
    

.src_container { background-color: rgb(231, 229, 220); width: 99%; overflow: hidden; margin: 12px 0pt ! important; padding: 0px 3px 3px 0px; }.src_container .titlebar { background-color: rgb(212, 223, 255); border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(79, 129, 189) rgb(79, 129, 189) -moz-use-text-color; padding: 3px 24px; margin: 0pt; width: auto; line-height: 120%; overflow: hidden; text-align: left; font-size: 12px; }.src_container .toolbar { display: inline; font-weight: normal; font-size: 100%; float: right; color: rgb(0, 0, 255); text-align: left; overflow: hidden; }.toolbar span.button { display: inline; font-weight: normal; font-size: 100%; color: rgb(0, 0, 255); text-align: left; overflow: hidden; cursor: pointer; }.src_container div.clientarea { background-color: white; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; height: auto; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,courier,monospace,serif; }.src_container ol.mainarea { padding: 0pt 0pt 0pt 52px; margin: 0pt; background-color: rgb(247, 247, 255) ! important; }.number_show { padding-left: 52px ! important; list-style: decimal outside none ! important; }.number_show li { list-style: decimal outside none ! important; border-left: 1px dotted rgb(79, 129, 189); }.number_hide { padding-left: 0px ! important; list-style-type: none ! important; }.number_hide li { list-style-type: none ! important; border-left: 0px none; }ol.mainarea li { display: list-item ! important; font-size: 12px ! important; margin: 0pt ! important; line-height: 18px ! important; padding: 0pt 0pt 0pt 0px ! important; background-color: rgb(247, 247, 255) ! important; color: rgb(79, 129, 189); }ol.mainarea li pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap ol.mainarea li pre { white-space: pre-wrap; word-wrap: break-word; }ol.mainarea li pre.alt { background-color: rgb(247, 247, 255) ! important; }4、创建用于实现验证逻辑的ConfirmValidator类

显示行号 复制代码 ConfirmValidator
  1. public class ConfirmValidator : ModelValidator
    
  2. {
    
  3.     private string  confirmPropertyName;
    
  4.     public ConfirmValidator(ModelMetadata metaData, ControllerContext context, string confirmProperty)
    
  5.         : base(metaData, context)
    
  6.     {
    
  7.         confirmPropertyName = confirmProperty;
    
  8.     }
    
  9.     public override IEnumerable<ModelValidationResult> Validate(object container)
    
  10.     {
    
  11.         if (container == null)
    
  12.             yield break;
    
  13.         PropertyInfo pi = container.GetType().GetProperty(confirmPropertyName);
    
  14.         if (pi != null)
    
  15.         {
    
  16.             string confirmValue = (string)pi.GetValue(container, null);
    
  17.             if ( !(Metadata.Model??String.Empty).Equals(confirmValue??String.Empty))
    
  18.             {
    
  19.                 yield return new ModelValidationResult()
    
  20.                 {
    
  21.                     Message = "两次输入不一致!"
    
  22.                 };
    
  23.             }
    
  24.         }
    
  25.         else
    
  26.         {
    
  27.             throw new InvalidOperationException("属性" + confirmPropertyName + "不存在");
    
  28.         }
    
  29.     }
    
  30. }
    

.src_container { background-color: rgb(231, 229, 220); width: 99%; overflow: hidden; margin: 12px 0pt ! important; padding: 0px 3px 3px 0px; }.src_container .titlebar { background-color: rgb(212, 223, 255); border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(79, 129, 189) rgb(79, 129, 189) -moz-use-text-color; padding: 3px 24px; margin: 0pt; width: auto; line-height: 120%; overflow: hidden; text-align: left; font-size: 12px; }.src_container .toolbar { display: inline; font-weight: normal; font-size: 100%; float: right; color: rgb(0, 0, 255); text-align: left; overflow: hidden; }.toolbar span.button { display: inline; font-weight: normal; font-size: 100%; color: rgb(0, 0, 255); text-align: left; overflow: hidden; cursor: pointer; }.src_container div.clientarea { background-color: white; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; height: auto; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,courier,monospace,serif; }.src_container ol.mainarea { padding: 0pt 0pt 0pt 52px; margin: 0pt; background-color: rgb(247, 247, 255) ! important; }.number_show { padding-left: 52px ! important; list-style: decimal outside none ! important; }.number_show li { list-style: decimal outside none ! important; border-left: 1px dotted rgb(79, 129, 189); }.number_hide { padding-left: 0px ! important; list-style-type: none ! important; }.number_hide li { list-style-type: none ! important; border-left: 0px none; }ol.mainarea li { display: list-item ! important; font-size: 12px ! important; margin: 0pt ! important; line-height: 18px ! important; padding: 0pt 0pt 0pt 0px ! important; background-color: rgb(247, 247, 255) ! important; color: rgb(79, 129, 189); }ol.mainarea li pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap ol.mainarea li pre { white-space: pre-wrap; word-wrap: break-word; }ol.mainarea li pre.alt { background-color: rgb(247, 247, 255) ! important; }5、创建用于产生ConfirmValidator的提供者类:ConfirmValidatorProvider

显示行号 复制代码 ConfirmValidatorProvider
  1. public class ConfirmValidatorProvider : AssociatedValidatorProvider
    
  2. {
    
  3.     protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    
  4.     {
    
  5.         foreach (ConfirmValidatorAttribute attr in attributes.OfType<ConfirmValidatorAttribute>())
    
  6.         {
    
  7.             yield return new ConfirmValidator(metadata, context, attr.ConfirmPropertyName);
    
  8.         }
    
  9.     }
    
  10. }
    

.src_container { background-color: rgb(231, 229, 220); width: 99%; overflow: hidden; margin: 12px 0pt ! important; padding: 0px 3px 3px 0px; }.src_container .titlebar { background-color: rgb(212, 223, 255); border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(79, 129, 189) rgb(79, 129, 189) -moz-use-text-color; padding: 3px 24px; margin: 0pt; width: auto; line-height: 120%; overflow: hidden; text-align: left; font-size: 12px; }.src_container .toolbar { display: inline; font-weight: normal; font-size: 100%; float: right; color: rgb(0, 0, 255); text-align: left; overflow: hidden; }.toolbar span.button { display: inline; font-weight: normal; font-size: 100%; color: rgb(0, 0, 255); text-align: left; overflow: hidden; cursor: pointer; }.src_container div.clientarea { background-color: white; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; height: auto; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,courier,monospace,serif; }.src_container ol.mainarea { padding: 0pt 0pt 0pt 52px; margin: 0pt; background-color: rgb(247, 247, 255) ! important; }.number_show { padding-left: 52px ! important; list-style: decimal outside none ! important; }.number_show li { list-style: decimal outside none ! important; border-left: 1px dotted rgb(79, 129, 189); }.number_hide { padding-left: 0px ! important; list-style-type: none ! important; }.number_hide li { list-style-type: none ! important; border-left: 0px none; }ol.mainarea li { display: list-item ! important; font-size: 12px ! important; margin: 0pt ! important; line-height: 18px ! important; padding: 0pt 0pt 0pt 0px ! important; background-color: rgb(247, 247, 255) ! important; color: rgb(79, 129, 189); }ol.mainarea li pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap ol.mainarea li pre { white-space: pre-wrap; word-wrap: break-word; }ol.mainarea li pre.alt { background-color: rgb(247, 247, 255) ! important; }6、创建用于测试的控制器及视图

显示行号 复制代码 HomeController
  1. public class HomeController : Controller
    
  2. {
    
  3.     public ActionResult Index()
    
  4.     {
    
  5.         return View(new UserInfo());
    
  6.     }
    
  7.     [HttpPost]
    
  8.     public ActionResult Index(UserInfo ui)
    
  9.     {
    
  10.         return View(ui);
    
  11.     }
    
  12. }
    
<body>
    <div>
    <%using(Html.BeginForm()){ %>
        <%= Html.ValidationSummary(true) %>
        <%= Html.EditorFor(x=>x) %>
        <input type="submit" value="提交" />
    <%} %>
    </div>
</body>

.codearea { color: black; background-color: white; line-height: 18px; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,”BitStream Vera Sans Mono”,courier,monospace,serif; }.codearea pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap pre { white-space: pre-wrap; word-wrap: break-word; }.codearea pre.alt { background-color: rgb(247, 247, 255) ! important; }.codearea .lnum { color: rgb(79, 129, 189); line-height: 18px; }
 

7、修改UserInfo.cs,在ConfirmPassword属性上添加ConfirmValidator特性。

[ConfirmValidator("Password")]
public string ConfirmPassword { get; set; }

.codearea { color: black; background-color: white; line-height: 18px; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,”BitStream Vera Sans Mono”,courier,monospace,serif; }.codearea pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap pre { white-space: pre-wrap; word-wrap: break-word; }.codearea pre.alt { background-color: rgb(247, 247, 255) ! important; }.codearea .lnum { color: rgb(79, 129, 189); line-height: 18px; }

8、在Global Application_Start中添加ConfirmValidatorProvider

ModelValidatorProviders.Providers.Add(new ConfirmValidatorProvider());

实现客户端验证:

1、修改ConfirmValidator类,添加GetClientValidationRules方法。

public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
    ModelClientValidationRule rule = new ModelClientValidationRule()
    {
        ErrorMessage = "两次输入不一致!",
        ValidationType = "ConfirmValidator"
    };
    rule.ValidationParameters["ConfirmPropertyName"] = confirmPropertyName;
    yield return rule;
}

.codearea { color: black; background-color: white; line-height: 18px; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,”BitStream Vera Sans Mono”,courier,monospace,serif; }.codearea pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap pre { white-space: pre-wrap; word-wrap: break-word; }.codearea pre.alt { background-color: rgb(247, 247, 255) ! important; }.codearea .lnum { color: rgb(79, 129, 189); line-height: 18px; }2、修改Index.aspx,添加对Ajax脚本的引用

<script type="text/javascript" src="http://www.cnblogs.com/Scripts/MicrosoftAjax.js"></script>
<script type="text/javascript" src="http://www.cnblogs.com/Scripts/MicrosoftMvcValidation.js"></script>

3、添加自定义验证脚本

<script type="text/javascript">
    Sys.Mvc.ValidatorRegistry.validators.ConfirmValidator = function (rule) {
        var propertyName = rule.ValidationParameters.ConfirmPropertyName;
        return function (value, context) {
            var confirmValue = document.getElementsByName(propertyName)[0].value;
            return (value == confirmValue);
        }
    };
</script>

.codearea { color: black; background-color: white; line-height: 18px; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,”BitStream Vera Sans Mono”,courier,monospace,serif; }.codearea pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap pre { white-space: pre-wrap; word-wrap: break-word; }.codearea pre.alt { background-color: rgb(247, 247, 255) ! important; }.codearea .lnum { color: rgb(79, 129, 189); line-height: 18px; }
4、开启客户端验证功能

<%Html.EnableClientValidation(); %>

5、将ConfirmPassword属性加入客户端验证

<% Html.ValidateFor(x => x.ConfirmPassword); %>

修改后完整视图代码:

<body>
    <div>
    <%Html.EnableClientValidation(); %>
    <%using(Html.BeginForm()){ %>
        <%= Html.ValidationSummary(true) %>
        <% Html.ValidateFor(x => x.ConfirmPassword); %>
        <%= Html.EditorFor(x=>x) %>
        <input type="submit" value="提交" />
    <%} %>
    </div>
    <script type="text/javascript" src="http://www.cnblogs.com/Scripts/MicrosoftAjax.js"></script>
    <script type="text/javascript" src="http://www.cnblogs.com/Scripts/MicrosoftMvcValidation.js"></script>
    <script type="text/javascript">
        Sys.Mvc.ValidatorRegistry.validators.ConfirmValidator = function (rule) {
            var propertyName = rule.ValidationParameters.ConfirmPropertyName;
            return function (value, context) {
                var confirmValue = document.getElementsByName(propertyName)[0].value;
                return (value == confirmValue);
            }
        };
    </script>
</body>

源代码下载

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏