zoukankan      html  css  js  c++  java
  • MVC中的扩展点(九)验证

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

        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. }
      
    8. 
      

    3、创建一个特性,用于指定与属性关联的另一个属性的名称

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

    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. 
      
    10.     public override IEnumerable<ModelValidationResult> Validate(object container)
      
    11.     {
      
    12.         if (container == null)
      
    13.             yield break;
      
    14.         PropertyInfo pi = container.GetType().GetProperty(confirmPropertyName);
      
    15.         if (pi != null)
      
    16.         {
      
    17.             string confirmValue = (string)pi.GetValue(container, null);
      
    18.             if ( !(Metadata.Model??String.Empty).Equals(confirmValue??String.Empty))
      
    19.             {
      
    20.                 yield return new ModelValidationResult()
      
    21.                 {
      
    22.                     Message = "两次输入不一致!"
      
    23.                 };
      
    24.             }
      
    25.         }
      
    26.         else
      
    27.         {
      
    28.             throw new InvalidOperationException("属性" + confirmPropertyName + "不存在");
      
    29.         }
      
    30.     }
      
    31. }
      
    32. 
      

    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. }
      
    11. 
      

    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. }
      
    13. 
      
    <body>
        <div>
        <%using(Html.BeginForm()){ %>
            <%= Html.ValidationSummary(true) %>
            <%= Html.EditorFor(x=>x) %>
            <input type="submit" value="提交" />
        <%} %>
        </div>
    </body>

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

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

    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;
    }
     

    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>

    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>

    源代码下载

  • 相关阅读:
    AtCoder Beginner Contest 205
    Codeforces Round #725 (Div. 3)
    Educational Codeforces Round 110 (Rated for Div. 2)【A
    Codeforces Round #722 (Div. 2)
    AtCoder Beginner Contest 203(Sponsored by Panasonic)
    AISing Programming Contest 2021(AtCoder Beginner Contest 202)
    PTA 520 钻石争霸赛 2021
    Educational Codeforces Round 109 (Rated for Div. 2)【ABCD】
    AtCoder Beginner Contest 200 E
    Educational Codeforces Round 108 (Rated for Div. 2)【ABCD】
  • 原文地址:https://www.cnblogs.com/xfrog/p/1944867.html
Copyright © 2011-2022 走看看