Asp.net MVC源码分析--Model Validation(Server端)实现(2)
2011-12-17 01:21
801 查看
前面我们介绍了Model Validation的用法,以及ValidateModel的方法实现,这一篇我们来详细学习一下DataAnnotationsModelValidatorProvider类的实现。
上一篇:http://www.cnblogs.com/RobbinHan/archive/2011/12/15/2289228.html
请参照DataAnnotationsModelValidator.Validate 方法源码,第7行代码,就是在这里进行了适配的工作。
我们看到从16-22行, MVC会从属性的Attributes中找到与AttributeFactories匹配的DataAnnotationsModelValidationFactory, 并调用返回ModelValidator对象,但如果没有从AttributeFactories找到匹配的对象,则使用DefaultAttributeFactory 委托创建一个ModelValidator对象。
DefaultAttributeFactory内部实现是创建一个DataAnnotationsModelValidator对象在构造时传入ValidationAttribute, 实际上这个类也是RangeAttributeAdapter等*AttributeAdapter类的基类,它们初始化的逻辑也是一样的。
在这个方法中同样的我们发现MVC利用ValidatableObjectAdapter适配器连接ModelValidator.Validate方法 和 IValidatableObject.Validate接口.
BTW:利用[b]IValidatableObject接口可以实现验证模型的各个属性之间的逻辑关系.[/b]
可以参考另一篇文章:/article/5587517.html
通过对DataAnnotationsModelValidatorProvider.GetValidators 方法的分析我们得出:
如果Model中的属性如果是复杂对象时,即使子对象中标记了*ValidationAttribute也是不会验证的。
例如下面的代码:LogOnModel.ChangePassword 对象中的属性虽然标记了[Required]但是还是不会验证的。
转载请注明出处:http://www.cnblogs.com/RobbinHan/archive/2011/12/15/2289403.html
本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan
上一篇:http://www.cnblogs.com/RobbinHan/archive/2011/12/15/2289228.html
三.DataAnnotationsModelValidatorProvider类详解
1.AttributeFactories对象
首先在这个类中可以看到在初始化时创建了AttributeFactories对象(Dictionary), 这个集合包含了系统内置一些验证规则。internal static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories = new Dictionary<Type, DataAnnotationsModelValidationFactory>() { { typeof(RangeAttribute), (metadata, context, attribute) => new RangeAttributeAdapter(metadata, context, (RangeAttribute)attribute) }, { typeof(RegularExpressionAttribute), (metadata, context, attribute) => new RegularExpressionAttributeAdapter(metadata, context, (RegularExpressionAttribute)attribute) }, { typeof(RequiredAttribute), (metadata, context, attribute) => new RequiredAttributeAdapter(metadata, context, (RequiredAttribute)attribute) }, { typeof(StringLengthAttribute), (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute) }, } }
2.ValidationAttribte 的 Adapter 设计模式应用
这里特别需要注意的是MVC利用了*AttributeAdapter 把 ValidationAttribte 的GetValidationResult方法和 ModelValidator.Validate方法作了一个适配(这里用到Adapter模式)请看RangeAttributeAdapter/RegularExpressionAttributeAdapter/RequiredAttributeAdapter/StringLengthAttributeAdapter请参照DataAnnotationsModelValidator.Validate 方法源码,第7行代码,就是在这里进行了适配的工作。
public override IEnumerable<ModelValidationResult> Validate(object container) { // Per the WCF RIA Services team, instance can never be null (if you have // no parent, you pass yourself for the "instance" parameter). ValidationContext context = new ValidationContext(container ?? Metadata.Model, null, null); context.DisplayName = Metadata.GetDisplayName(); ValidationResult result = Attribute.GetValidationResult(Metadata.Model, context); if (result != ValidationResult.Success) { yield return new ModelValidationResult { Message = result.ErrorMessage }; } }
3.获取ModelValidator对象集合
接下来我们来分析一下DataAnnotationsModelValidatorProvider.GetValidators 方法的实现protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) { _adaptersLock.EnterReadLock(); try { List<ModelValidator> results = new List<ModelValidator>(); // Add an implied [Required] attribute for any non-nullable value type, // unless they've configured us not to do that. if (AddImplicitRequiredAttributeForValueTypes && metadata.IsRequired && !attributes.Any(a => a is RequiredAttribute)) { attributes = attributes.Concat(new[] { new RequiredAttribute() }); } // Produce a validator for each validation attribute we find foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>()) { DataAnnotationsModelValidationFactory factory; if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory)) { factory = DefaultAttributeFactory; } results.Add(factory(metadata, context, attribute)); } // Produce a validator if the type supports IValidatableObject if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType)) { DataAnnotationsValidatableObjectAdapterFactory factory; if (!ValidatableFactories.TryGetValue(metadata.ModelType, out factory)) { factory = DefaultValidatableFactory; } results.Add(factory(metadata, context)); } return results; } finally { _adaptersLock.ExitReadLock(); } }
我们看到从16-22行, MVC会从属性的Attributes中找到与AttributeFactories匹配的DataAnnotationsModelValidationFactory, 并调用返回ModelValidator对象,但如果没有从AttributeFactories找到匹配的对象,则使用DefaultAttributeFactory 委托创建一个ModelValidator对象。
DefaultAttributeFactory内部实现是创建一个DataAnnotationsModelValidator对象在构造时传入ValidationAttribute, 实际上这个类也是RangeAttributeAdapter等*AttributeAdapter类的基类,它们初始化的逻辑也是一样的。
internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory = (metadata, context, attribute) => new DataAnnotationsModelValidator(metadata, context, attribute);
4.IValidatableObject接口
最后我们看一下DataAnnotationsModelValidatorProvider.GetValidators 方法第25-30行代码.在这个方法中同样的我们发现MVC利用ValidatableObjectAdapter适配器连接ModelValidator.Validate方法 和 IValidatableObject.Validate接口.
BTW:利用[b]IValidatableObject接口可以实现验证模型的各个属性之间的逻辑关系.[/b]
可以参考另一篇文章:/article/5587517.html
总结:
Model Validatoin 可以通过在Modol属性上加入*ValidationAttribute 和 实现IValidatableObject 接口来进行验证通过对DataAnnotationsModelValidatorProvider.GetValidators 方法的分析我们得出:
如果Model中的属性如果是复杂对象时,即使子对象中标记了*ValidationAttribute也是不会验证的。
例如下面的代码:LogOnModel.ChangePassword 对象中的属性虽然标记了[Required]但是还是不会验证的。
public class ChangePasswordModel { [Required] [DataType(DataType.Password)] [Display(Name = "Current password")] public string OldPassword { get; set; } [Required] [ValidatePasswordLength] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } public class LogOnModel : IValidatableObject { [Required] [Display(Name = "User name")] public string UserName { get; set; } [Required] [Display(Name = "User age")] public string Age { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [Display(Name = "Remember me?")] public bool RememberMe { get; set; } [Display(Name = "ChangePassword")] //[Required] public ChangePasswordModel ChangePassword { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { return Enumerable.Empty<ValidationResult>(); } }
疑问
另外有一点迷惑的是如果,如果把LogOnModel.ChangePassword属性是标记上[Required],而不从前台传入这个对象的话,所有LogOnModel属性上的验证都会失败,这一点不知道是不是Asp.net MVC 的bug,如果有人遇到请告诉我。转载请注明出处:http://www.cnblogs.com/RobbinHan/archive/2011/12/15/2289403.html
本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan
相关文章推荐
- Asp.net MVC源码分析--Model Validation(Server端)实现(1)
- asp.net MVC 模拟实现与源码分析
- Asp.net MVC源码分析--Model Validation(Client端)实现(2)
- Asp.net MVC源码分析--Model Validation(Client端)实现(1)
- [ASP.NET]分析MVC5源码,并实现一个ASP.MVC
- [ASP.NET]分析MVC5源码,并实现一个ASP.MVC
- ASP.NET WebForm / MVC 源码分析
- asp.net mvc源码分析-Controllerl篇 如何创建Controller实例
- asp.net mvc 之旅 —— 第五站 从源码中分析asp.net mvc 中的TempData
- asp.net mvc源码分析-Action篇 Action的执行
- Entity Framework在Asp.net MVC中的实现One Context Per Request(附源码)
- asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证
- asp.net mvc源码分析-ModelValidatorProviders
- asp.net mvc源码分析 - 路由(Routing)
- asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证
- asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析
- asp.net mvc源码分析-Action篇 Action的执行
- asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析
- ASP.NET MVC源码分析系列
- ASP.NET MVC 3仿Server.Transfer效果的实现方法