對於ASP.NET MVC基於標注特性的Model驗證,很多人只知道應用在數據類型及其屬性上用於定義驗證規則和錯誤消息的ValidationAttribute。通過《ASP.NET MVC以ModelValidator為核心的Model驗證體系: ModelValidator》的介紹,我們知道了最終用於進行Model驗證的是一個叫做ModelValidator的組件。ValidationAttribute對應的ModelValidator為DataAnnotationsModelValidator,這篇簡短的文章為你介紹ASP.NET MVC是如何針對Validation來創建DataAnnotationsModelValidator,以及後者如何利用前者實施Model驗證的。
一、DataAnnotationsModelValidator
ModelValidator是真正用於進行Model驗證的組件,上面介紹的驗證特性最終被封裝成DataAnnotationsModelValidator對象進而被應用到Model驗證系統中。如下面的代碼片斷所示,被封裝的ValidationAttribute通過只讀屬性Attribute表示,該屬性在構造函數中被初始化。
1: public class DataAnnotationsModelValidator : ModelValidator
2: {
3: public DataAnnotationsModelValidator(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute);
4: public override IEnumerable<ModelClientValidationRule> GetClientValidationRules();
5:
6: public override IEnumerable<ModelValidationResult> Validate(object container)
7: {
8: ValidationContext validationContext = new ValidationContext(container ?? this.Metadata.Model, null, null)
9: {
10: DisplayName = this.Metadata.GetDisplayName()
11: };
12: ValidationResult validationResult = this.Attribute.GetValidationResult(this.Metadata.Model, validationContext);
13: if (validationResult != ValidationResult.Success)
14: {
15: ModelValidationResult iteratorVariable2 = new ModelValidationResult
16: {
17: Message = validationResult.ErrorMessage
18: };
19: yield return iteratorVariable2;
20: }
21: else
22: {
23: yield break;
24: }
25: }
26: protected ValidationAttribute Attribute { get; }
27: protected string ErrorMessage { get; }
28: public override bool IsRequired { get; }
29: }
我們給出了用於實施驗證的核心方法Validate的完整定義。在該方法中,基於被驗證對象(如果為Null則采用Model元數據的Model屬性)創建出表示當前驗證上下文的ValidationContext對象,並采用Model元數據的DisplayName屬性作為該上下文的顯示名稱。最後直接調用被封裝的ValidationAttribute的GetValidationResult方法對指定對象實施驗證,如果返回的ValidationResult對象不為空,則以此創建ModelValidationResult對象並返回。
順便在說說定義在DataAnnotationsModelValidator中的另外兩個受保護只讀屬性的邏輯。用於返回錯誤消息的ErrorMessage屬性來源對對ValidationAttribute的FormatErrorMessage方法的調用,而指定的參數就是當前Model元數據的DisplayName屬性。由於只有RequiredAttribute才用於必需字段的驗證,所有只有被封裝ValidationAttribute為RequiredAttribute時其IsRequired屬性返回True。
二、DataAnnotationsModelValidator<TAttribute>
DataAnnotationsModelValidator<TAttribute>是DataAnnotationsModelValidator的子類,其泛型參數為相應的ValidationAttribute的類型,下面的代碼片斷反映了其定義:
1: public class DataAnnotationsModelValidator<TAttribute> : DataAnnotationsModelValidator where TAttribute: ValidationAttribute
2: {
3: public DataAnnotationsModelValidator(ModelMetadata metadata, ModelBindingExecutionContext context, TAttribute attribute);
4: protected TAttribute Attribute { get; }
5: }
作為DataAnnotationsModelValidator與相應ValidationAttribute之間的適配,ASP.NET MVC為常用的ValidationAttribute(RequiredAttribute、RangeAttribute、RegularExpressionAttribute和StringLengthAttribute)定義相應的適配類型。如下面的代碼片斷所示,它們都是泛型的DataAnnotationsModelValidator<TAttribute>的子類。當我們將這些ValidationAttribute應用到Model類型時,真正用於Model驗證的實際上就是這些作為適配的ModelValidator。
1: public class RequiredAttributeAdapter : DataAnnotationsModelValidator<RequiredAttribute>
2: {
3: public RequiredAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredAttribute attribute);
4: public override IEnumerable<ModelClientValidationRule> GetClientValidationRules();
5: }
6:
7: public class RangeAttributeAdapter : DataAnnotationsModelValidator<RangeAttribute>
8: {
9: public RangeAttributeAdapter(ModelMetadata metadata, ControllerContext context, RangeAttribute attribute);
10: public override IEnumerable<ModelClientValidationRule> GetClientValidationRules();
11: }
12:
13: public class RegularExpressionAttributeAdapter : DataAnnotationsModelValidator<RegularExpressionAttribute>
14: {
15: public RegularExpressionAttributeAdapter(ModelMetadata metadata, ControllerContext context, RegularExpressionAttribute attribute);
16: public override IEnumerable<ModelClientValidationRule> GetClientValidationRules();
17: }
18:
19: public class StringLengthAttributeAdapter : DataAnnotationsModelValidator<StringLengthAttribute>
20: {
21: public StringLengthAttributeAdapter(ModelMetadata metadata, ControllerContext context, StringLengthAttribute attribute);
22: public override IEnumerable<ModelClientValidationRule>GetClientValidationRules();
23: }