zoukankan      html  css  js  c++  java
  • 在ASP.NET MVC中如何应用多个相同类型的ValidationAttribute?

    ASP.NET MVC采用System.ComponentModel.DataAnnotations提供的元数据验证机制对Model实施验证,我们可以在Model类型或者字段/属性上应用相应的ValidationAttribute。但是在默认情况下,对于同一个类型的ValidationAttribute特性只允许一个应用到目标元素上——即使我们将AllowMultiple属性设置为True。这篇文章的目的就是为了解决这个问题。[源代码从这里下载]

    一、一个自定义ValidationAttribute:RangeIfAttribute

    为了演示在相同的目标元素(类、属性或者字段)应用多个同类的ValidationAttribute,我定义了一个名称为RangeIfAttribute特性用于进行“有条件的区间验证”。如下面的代码片断所示,RangeIfAttribute是RangeAttribute的子类,应用在上面的AttributeUsageAttribute特性的AllowMultiple 属性被设置为True。RangeIfAttribute定义了Property和Value两个属性,分别表示被验证属性/字段所在类型的另一个属性名称和相应的值,只有当指定的属性值与通过Value属性值相等的情况下我们在真正进行验证。具体的验证逻辑定义在重写的IsValid方法中。

       1: [AttributeUsage( AttributeTargets.Field| AttributeTargets.Property, AllowMultiple = true)]
       2: public class RangeIfAttribute: RangeAttribute
       3: {
       4:     public string Property { get; set; }
       5:     public string Value { get; set; }
       6:     public RangeIfAttribute(string property, string value, double minimum, double maximum)
       7:         : base(minimum, maximum)
       8:     {
       9:         this.Property = property;
      10:         this.Value = value;
      11:     }
      12:     protected override ValidationResult IsValid(object value, ValidationContext validationContext)
      13:     {
      14:         object propertyValue = validationContext.ObjectType.GetProperty(this.Property).GetValue(validationContext.ObjectInstance,null);
      15:         propertyValue = propertyValue ?? "";
      16:         if (propertyValue.ToString()!= this.Value)
      17:         {
      18:             return null;
      19:         }
      20:         if (base.IsValid(value))
      21:         {
      22:             return null;
      23:         }
      24:  
      25:         string[] memberNames = (validationContext.MemberName != null) ? new string[] { validationContext.MemberName } : null;
      26:         return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName), memberNames);
      27:     }
      28: }

    二、将RangeIfAttribute应用于Employee

    我们将RangeIfAttribute特性应在具有如下定义的表示员工的Employee类型的Salary(表示薪水)属性上,另外一个属性Grade表示员工的级别。应用在Salary属性上的RangeIfAttribute特性体现了基于级别的薪水区间验证规则:对于G7、G8和G9的员工,其薪水分别在2000~3000,3000~4000和4000~5000范围内。

       1: public class Employee
       2: {
       3:     public string Name { get; set; }
       4:     public string Grad { get; set; }
       5:     [RangeIf("Grad", "G7", 2000, 3000)]
       6:     [RangeIf("Grad", "G8", 3000, 4000)]
       7:     [RangeIf("Grad", "G9", 4000, 5000)]
       8:     public decimal Salary { get; set; }
       9: }

    现在我们创建如下一个EmployeeController,其默认的两个Index操作方法定义如下。在HttpPost的Index操作中,如果验证成功我们将“验证成功”字样作为ModelError添加到ModelState中。

       1: public class EmployeeController : Controller
       2: {
       3:     public ActionResult Index()
       4:     {
       5:         return View(new Employee());
       6:     }
       7:  
       8:     [HttpPost]
       9:     public ActionResult Index(Employee employee)
      10:     {
      11:         if (ModelState.IsValid)
      12:         {
      13:             ModelState.AddModelError("", "验证成功");
      14:             return View(new Employee());
      15:         }
      16:         else
      17:         {
      18:             return View(new Employee());
      19:         }
      20:     }
      21:  
      22: }

    下面是Index操作默认的View的定义:

       1: @model MultipleValidator.Models.Employee
       2: @{
       3:     ViewBag.Title = "Employee Management";
       4: }
       5: @Html.ValidationSummary(true)
       6: @using (Html.BeginForm())
       7: { 
       8:     @Html.EditorForModel()
       9:     <input type="submit" value="Save" />
      10: }

    遗憾的是,ASP.NET MVC并不能按照我们希望的方对我们的输入进行验证。如下面的截图所示,我们只有在输入G9的时候,系统才能实施成功地验证,对于G7和G8则被输入的Salary值(0.00)是合法的。

    image

    三、重写TypeId属性解决问题

    之所以会发生上述的这种现象,原因在于被应用到Salary属性上的RangeIfAttribute特性,最终只有最后一个(Value=“G9”)被使用到。ASP.NET MVC在生成包括验证特性的Model的元数据的时候,针对某个元素的所有ValidationAttribute是被维护在一个字典上的,而这个字典的值就是Attribute的TypeId属性。在默认的情况下,Attribute的TypeId返回的是自身的类型,所以导致应用到相同目标元素的同类ValidationAttribute只能有一个。幸好Attribute的TypeId属性是可以被重写的,县在我们在RangeIfAttribute中按照如下的方式对这个属性进行重写:

       1: [AttributeUsage( AttributeTargets.Field| AttributeTargets.Property, AllowMultiple = true)]
       2: public class RangeIfAttribute: RangeAttribute
       3: {
       4:     //其他成员
       5:     private object typeId;
       6:     public override object TypeId
       7:     {
       8:         get
       9:         {
      10:             return (null == typeId) ? (typeId = new object()) : typeId;
      11:         }
      12:     }
      13: }

    再次运行我们的程序则一切正常:

    image

    值得一提的是:重写TypeId属性的方式只能解决服务端验证的问题,对于客户端认证无效。

  • 相关阅读:
    流处理 —— Spark Streaming中的Window操作
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.8 提供带注解的限定符元数据
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.7 为自动检测组件提供作用域
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.6 给自动检测组件命名
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.5 在组件中定义bean的元数据
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.4 使用过滤器自定义扫描
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.3 自动检测类和注册bean的定义
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.2 元注解
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.1 @Component和深层的构造型注解
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10 类路径扫描和被管理的组件
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2321672.html
Copyright © 2011-2022 走看看