加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则

发布时间:2020-12-16 09:05:20 所属栏目:asp.Net 来源:网络整理
导读:对于Model验证,理想的设计应该是场景驱动的,而不是Model(类型)驱动的,也就是对于同一个Model对象,在不同的使用场景中可能具有不同的验证规则。举个简单的例子,对于一个表示应聘者的数据对象来说,针对应聘的岗位不同,肯定对应聘者的年龄、性别、专业

对于Model验证,理想的设计应该是场景驱动的,而不是Model(类型)驱动的,也就是对于同一个Model对象,在不同的使用场景中可能具有不同的验证规则。举个简单的例子,对于一个表示应聘者的数据对象来说,针对应聘的岗位不同,肯定对应聘者的年龄、性别、专业技能等方面有不同的要求。但是ASP.NET MVC的Model验证确是Model驱动的,因为验证规则以验证特性的形式应用到Model类型及其属性上。这样的验证方式实际上限制了Model类型在基于不同验证规则的使用场景中的重用。通过上一篇文章《将ValidationAttribute应用到参数上》的扩展我们将验证特性直接应用在参数上变成了可能,这从一定程度上解决了这个问题,但是只能解决部分问题,因为应用到参数的验证特性只能用于针对参数类型级别的验证,而不能用于针对参数类型属性级别的验证(源代码从这里下载)。[本文已经同步到《How ASP.NET MVC Works?》中]

目录
一、同一个Model在采用不同的验证规则
二、新的基类ValidatorAttribute
三、指定当前采用的验证规则:ValidationRuleAttribute
四、新的Controller基类:RuleBasedController
五、自定义ModelValidatorProvider:RuleBasedValidatorProvider

一、同一个Model在采用不同的验证规则

现在我们通过利用对ASP.NET MVC的扩展来实现一种基于不同验证规则的Model验证。为了让读者对这种认证方式有一个感官的认识,我们来看看这个扩展最终实现怎样的验证效果。在通过Visual Studio的ASP.NET MVC 项目模板创建的空Web应用中,我们定义了如下一个Person类型作为Model。

   1: public class Person
   3:     [DisplayName("姓名")]
   5:? 
   7:     string Gender { get; set; }
   9:     [DisplayName("年龄")]
  11:     [RangeValidator(20,30,1)">"Rule2",1)">"{0}必须在{1}和{2}之间!")]
  14: }

在表示年龄的Age属性上应用了三个RangeValidatorAttribute(不是RangeAttribute),它们对应针对年龄的三种不同的验证规则,RuleName属性表示规则名称。三种验证规则(Rule1、Rule2和Rule3)分别要求年龄分别在10到20、20到30和30到40岁之间

然后我们定义了具有如下定义HomeController,它具有三组Action方法(Index、Rule1和Rule2)。方法Rule1、Rule2和HomeController类上应用了一个ValidationRuleAttribute特性用于指定了当前采用的验证规则。用于指定验证规则的ValidationRuleAttribute特性可以同时应用于Controller类型和Action方法上,应用于后者的ValidationRuleAttribute特性具有更高的优先级。针对HomeController的定义,Action方法Index、Rule1和Rule2分别采用的验证规则为Rule3、Rule1和Rule2。

   2: class HomeController : RuleBasedController
public ActionResult Index()
   6:         return View("person",new Person());
   8:     [HttpPost]
  10:     {
  12:     }
  14:     [ValidationRule("Rule1")]
  16:     {
  18:     }
  20:     [ValidationRule(  21:     public ActionResult Rule1(Person person)
  23:           24:     }
  26:     [ValidationRule("Rule2")]
  28:     {
  31:     [HttpPost]
  33:     public ActionResult Rule2(Person person)
  35:           36:     }
   1: @model Person
   3: { 
   5:     <input type="submit" value="保存" />
   1: [AttributeUsage( AttributeTargets.Class| AttributeTargets.Property,AllowMultiple = true)]
private object typeId;
   6:     override object TypeId
   8:         get{return typeId ?? (typeId = new object());}
  10: }

上面演示实例采用的RangeValidatorAttribute定义如下,我们可以看到它仅仅是对RangeAttribute的封装。RangeValidatorAttribute具有与RangeAttribute一致的构造函数定义,并直接使用被封装的RangeAttribute实施验证。除了能够通过RuleName指定具体采用的验证规则之外,其他的使用方式与RangeAttribute完全一致。

private RangeAttribute rangeAttribute;
   6:     {
   8:     }
  11:         rangeAttribute = public RangeValidatorAttribute(Type type,1)">string minimum,1)">string maximum)
  15:         rangeAttribute = new RangeAttribute(type,minimum,1)" id="lnum16">  16:     }
  18:     {
  20:     }
  22:     string FormatErrorMessage(string name)
  24:         return string.Format(CultureInfo.CurrentCulture,1)">base.ErrorMessageString,1)">object[] { name,rangeAttribute.Minimum,rangeAttribute.Maximum });
  26: }

三、指定当前采用的验证规则:ValidationRuleAttribute

ValidatorAttribte的RuleName属性仅仅指定了验证特性采用的验证规则名称,当前应在采用的验证规则通过应用在Action方法或者Controller类型上的ValidationRuleAttribute特性还指定。如下所示的就是ValidationRuleAttribute的定义,它仅仅包含一个表示当前采用的验证规则名称的RuleName属性的特性而已。

class ValidationRuleAttribute: Attribute
public ValidationRuleAttribute(string ruleName)
   9: }

四、新的Controller基类:RuleBasedController

对于这个用于实现针对不同验证规则的扩展来说,其核心是如何将通过ValidationRuleAttribute特性设置的验证规则应用到ModelValidator的提供机制中,使之筛选出与当前验证规则匹配的验证特性,在这里我们依然使用Controller上下文来保存这个这个验证规则名称。细心的读者应该留意到了上面演示实例中创建的HomeController不是继承自Controller,而是继承自RuleBasedController,这个自定义的Controller基类定义如下。

3: static Dictionary<Type,ControllerDescriptor> controllerDescriptors = new Dictionary<Type,ControllerDescriptor>();
   6:         get
   8:             ControllerDescriptor controllerDescriptor;
  10:             {
  12:             }
  14:             {
  17:                     controllerDescriptor = new ReflectedControllerDescriptor(this.GetType());
  19:                 }
  21:             }
  23:     }
  25:     {
  27:         base.BeginExecuteCore(callback,state);
  29:     void ExecuteCore()
  31:         SetValidationRule();
  33:     }
  35:     {
  37:         ActionDescriptor actionDescriptor = this.ControllerDescriptor.FindAction(this.ControllerContext,actionName);
  39:         {
  41:                 this.ControllerDescriptor.GetCustomAttributes(true).OfType<ValidationRuleAttribute>().FirstOrDefault() ??
  43:             this.ControllerContext.RouteData.DataTokens.Add("ValidationRuleName",validationRuleAttribute.RuleName);
  45:     }
class RuleBasedValidatorProvider : DataAnnotationsModelValidatorProvider
   4:     {
   6:         context.RouteData.DataTokens.TryGetValue(out validationRuleName);
   8:         attributes = this.FilterAttributes(attributes,ruleName);
  10:     }
  12:     private IEnumerable<Attribute> FilterAttributes(IEnumerable<Attribute> attributes,1)">string validationRule)
  14:             var validatorAttributes = attributes.OfType<ValidatorAttribute>();
  16:             List<ValidatorAttribute> validValidatorAttributes = new List<ValidatorAttribute>();
  18:         string.IsNullOrEmpty(validationRule))
  20:             validValidatorAttributes.AddRange(validatorAttributes.Where(v => string.IsNullOrEmpty(v.RuleName)));                    
  22:         else
  24:             var groups = from validator in validatorAttributesgroup validator by validator.GetType();
  26:             {
  28:                 null != validatorAttribute)
  30:                     validValidatorAttributes.Add(validatorAttribute);
  32:                   33:                 {
  35:                     null != validatorAttribute)
  37:                         validValidatorAttributes.Add(validatorAttribute);
  39:                 }
  41:         }
  43:     }
class MvcApplication : System.Web.HttpApplication
void Application_Start()
   7:         DataAnnotationsModelValidatorProvider validator = ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().FirstOrDefault();
   9:         {
  11:         }
  13:     }