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