Asp.net MVC源碼分析–Model Validation(Server端)實現(2)

來源:互聯網
上載者:User
文章目錄
  • 1.AttributeFactories對象
  • 2.ValidationAttribte 的 Adapter 設計模式應用
  • 3.擷取ModelValidator對象集合
  • 4.IValidatableObject介面

前面我們介紹了Model Validation的用法,以及ValidateModel的方法實現,這一篇我們來詳細學習一下DataAnnotationsModelValidatorProvider類的實現。

上一篇:http://www.cnblogs.com/RobbinHan/archive/2011/12/15/2289228.html 

三.DataAnnotationsModelValidatorProvider類詳解1.AttributeFactories對象

首先在這個類中可以看到在初始化時建立了AttributeFactories對象(Dictionary),  這個集合包含了系統內建一些驗證規則。

 1         internal static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories = new Dictionary<Type, DataAnnotationsModelValidationFactory>() {
2 {
3 typeof(RangeAttribute),
4 (metadata, context, attribute) => new RangeAttributeAdapter(metadata, context, (RangeAttribute)attribute)
5 },
6 {
7 typeof(RegularExpressionAttribute),
8 (metadata, context, attribute) => new RegularExpressionAttributeAdapter(metadata, context, (RegularExpressionAttribute)attribute)
9 },
10 {
11 typeof(RequiredAttribute),
12 (metadata, context, attribute) => new RequiredAttributeAdapter(metadata, context, (RequiredAttribute)attribute)
13 },
14 {
15 typeof(StringLengthAttribute),
16 (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute)
17 },
18 }
19 }
2.ValidationAttribte 的 Adapter 設計模式應用

這裡特別需要注意的是MVC利用了*AttributeAdapter 把 ValidationAttribte 的GetValidationResult方法和 ModelValidator.Validate方法作了一個適配(這裡用到Adapter模式)請看RangeAttributeAdapter/RegularExpressionAttributeAdapter/RequiredAttributeAdapter/StringLengthAttributeAdapter

請參照DataAnnotationsModelValidator.Validate 方法源碼,第7行代碼,就是在這裡進行了適配的工作。

 1  public override IEnumerable<ModelValidationResult> Validate(object container) {
2 // Per the WCF RIA Services team, instance can never be null (if you have
3 // no parent, you pass yourself for the "instance" parameter).
4 ValidationContext context = new ValidationContext(container ?? Metadata.Model, null, null);
5 context.DisplayName = Metadata.GetDisplayName();
6
7 ValidationResult result = Attribute.GetValidationResult(Metadata.Model, context);
8 if (result != ValidationResult.Success) {
9 yield return new ModelValidationResult {
10 Message = result.ErrorMessage
11 };
12 }
13 }
3.擷取ModelValidator對象集合

接下來我們來分析一下DataAnnotationsModelValidatorProvider.GetValidators 方法的實現

 1  protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) {
2 _adaptersLock.EnterReadLock();
3
4 try {
5 List<ModelValidator> results = new List<ModelValidator>();
6
7 // Add an implied [Required] attribute for any non-nullable value type,
8 // unless they've configured us not to do that.
9 if (AddImplicitRequiredAttributeForValueTypes &&
10 metadata.IsRequired &&
11 !attributes.Any(a => a is RequiredAttribute)) {
12 attributes = attributes.Concat(new[] { new RequiredAttribute() });
13 }
14
15 // Produce a validator for each validation attribute we find
16 foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>()) {
17 DataAnnotationsModelValidationFactory factory;
18 if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory)) {
19 factory = DefaultAttributeFactory;
20 }
21 results.Add(factory(metadata, context, attribute));
22 }
23
24 // Produce a validator if the type supports IValidatableObject
25 if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType)) {
26 DataAnnotationsValidatableObjectAdapterFactory factory;
27 if (!ValidatableFactories.TryGetValue(metadata.ModelType, out factory)) {
28 factory = DefaultValidatableFactory;
29 }
30 results.Add(factory(metadata, context));
31 }
32
33 return results;
34 }
35 finally {
36 _adaptersLock.ExitReadLock();
37 }
38 }

我們看到從16-22行, MVC會從屬性的Attributes中找到與AttributeFactories匹配的DataAnnotationsModelValidationFactory, 並調用返回ModelValidator對象,但如果沒有從AttributeFactories找到匹配的對象,則使用DefaultAttributeFactory 委託建立一個ModelValidator對象。

DefaultAttributeFactory內部實現是建立一個DataAnnotationsModelValidator對象在構造時傳入ValidationAttribute, 實際上這個類也是RangeAttributeAdapter等*AttributeAdapter類的基類,它們初始化的邏輯也是一樣的。

1   internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory =
2 (metadata, context, attribute) => new DataAnnotationsModelValidator(metadata, context, attribute);
4.IValidatableObject介面

最後我們看一下DataAnnotationsModelValidatorProvider.GetValidators 方法第25-30行代碼. 

在這個方法中同樣的我們發現MVC利用ValidatableObjectAdapter適配器串連ModelValidator.Validate方法 和 IValidatableObject.Validate介面.

BTW:利用IValidatableObject介面可以實現驗證模型的各個屬性之間的邏輯關係.

 可以參考另一篇文章:http://www.cnblogs.com/bjs007/archive/2011/01/27/1946419.html

總結:
  1. Model Validatoin 可以通過在Modol屬性上加入*ValidationAttribute 和 實現IValidatableObject 介面來進行驗證
  2. 通過對DataAnnotationsModelValidatorProvider.GetValidators 方法的分析我們得出:
    如果Model中的屬性如果是複雜物件時,即使子物件中標記了*ValidationAttribute也是不會驗證的。

        例如下面的代碼:LogOnModel.ChangePassword 對象中的屬性雖然標記了[Required]但是還是不會驗證的。

 1  public class ChangePasswordModel
2 {
3 [Required]
4 [DataType(DataType.Password)]
5 [Display(Name = "Current password")]
6 public string OldPassword { get; set; }
7
8 [Required]
9 [ValidatePasswordLength]
10 [DataType(DataType.Password)]
11 [Display(Name = "New password")]
12 public string NewPassword { get; set; }
13
14 [DataType(DataType.Password)]
15 [Display(Name = "Confirm new password")]
16 [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
17 public string ConfirmPassword { get; set; }
18 }
19
20 public class LogOnModel : IValidatableObject
21 {
22 [Required]
23 [Display(Name = "User name")]
24 public string UserName { get; set; }
25 [Required]
26 [Display(Name = "User age")]
27 public string Age { get; set; }
28 [Required]
29 [DataType(DataType.Password)]
30 [Display(Name = "Password")]
31 public string Password { get; set; }
32 [Display(Name = "Remember me?")]
33 public bool RememberMe { get; set; }
34 [Display(Name = "ChangePassword")]
35 //[Required]
36 public ChangePasswordModel ChangePassword { get; set; }
37
38 public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
39 {
40 return Enumerable.Empty<ValidationResult>();
41 }
42 }
疑問 

另外有一點迷惑的是如果,如果把LogOnModel.ChangePassword屬性是標記上[Required],而不從前台傳入這個對象的話,所有LogOnModel屬性上的驗證都會失敗,這一點不知道是不是Asp.net MVC 的bug,如果有人遇到請告訴我。


轉載請註明出處:http://www.cnblogs.com/RobbinHan/archive/2011/12/15/2289403.html

本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.