好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelVali

ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelValidatorProviders

前面篇文章我们分别介绍用真正用于实施Model验证的ModelValidator(《 ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelValidator 》),以及用于提供ModelValidator的ModelValidatorProvider(《 ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelValidatorProvider 》),那么对于ASP.NET MVC的Model验证体系来说,最终是通过怎样的方式对ModelValidatorProvider进行注册,又是如何利用它们来创建相应的ModelValidator来实施Model验证的呢?这就是本篇文章论述的重点。[本文已经同步到《 How ASP.NET MVC Works? 》中]

目录 
一、ModelValidatorProviders 
二、ModelValidator、ModelValidatorProvider和ModelValidatorProviders 
三、CompositeModelValidator 
四、实例演示:探测CompositeModelValidator采用的验证行为

一、ModelValidatorProviders

我们通过静态类型 ModelValidatorProviders 对ModelValidatorProvider进行注册。如下面的代码片断所示,ModelValidatorProviders具有一个静态只读属性Providers,其类型为ModelValidatorProviderCollection,表示注册的基于整个Web应用范围的ModelValidatorProvider列表。

    1:   public   static   class  ModelValidatorProviders
    2:  {   
    3:       public   static  ModelValidatorProviderCollection Providers { get; }
    4:  }
    5:   
    6:   public   class  ModelValidatorProviderCollection : Collection<ModelValidatorProvider>
    7:  {   
    8:       public  ModelValidatorProviderCollection();
    9:       public  ModelValidatorProviderCollection(IList<ModelValidatorProvider> list);
   10:       public  IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context);   
   11:  }

值得一提的是,ModelValidatorProviderCollection定义了一个GetValidators方法用于返回一个通过集合中每个ModelValidatorProvider创建的ModelValidator集合。在这个方法中,指定的Model元数据和Controller上下文会被传入每个ModelValidatorProvider对象的GetValidators方法,得到的每个ModelValidator对象将会作为最终返回的ModelValidator集合的元素。

在默认的情况下,通过ModelValidatorProviders的Providers表示注册的ModelValidatorProvider列表会包含三个对象,对应着我们前面介绍的三种ModelValidatorProvider类型,即 DataAnnotationsModelValidatorProvider 、 ClientDataTypeModelValidatorProvider 和 DataErrorInfoPropertyModelValidator 。

如果我们需要添加一个自定义ModelValidatorProvider,可以直接将相应的对象添加到ModelValidatorProviders的Providers列表中。如果需要采用自定义ModelValidatorProvider来替换掉现有的ModelValidatorProvider,比如我们创建了一个扩展的DataAnnotationsModelValidatorProvider,还需要将现有的ModelValidatorProvider从该列表中移除。

实现在ModelValidatorProvider中的ModelValidator提供机制是基于Model元数据和Controller上下文的,实际上用于描述Model元数据的ModelMetadata类型同样定义了一个GetValidators方法用于根据指定的Controller上下文的所有ModelValidator对象。如下面的代码片断所示,该方法直接调用了通过ModelValidatorProviders的Providers属性表述的ModelValidatorProviderCollection对象的同名方法。

    1:   public   abstract   class  ModelValidator
    2:  {
    3:       //其他成员 
    4:       public   virtual  IEnumerable<ModelValidator> GetValidators(ControllerContext context)
    5:      {
    6:           return  ModelValidatorProviders.Providers.GetValidators( this , context);
    7:      }
    8:  }


二、ModelValidator、ModelValidatorProvider和ModelValidatorProviders

上面我们介绍用于进行Model验证的ModelValidator,用于提供ModelValidator的ModelValidatorProvider,以及用于注册ModelValidatorProvider的ModelValidatorProviders,整个ModelValidator的提供机制以此三类组件为核心,下图所示的UML体现了它们之间的关系。

三、CompositeModelValidator

虽然CompositeModelValidator仅仅是定义在程序集System.Web.Mvc.dll中的一个私有类型,但是它在ASP.NET MVC的Model验证系统中具有重要的地位,可以说真正用于Model验证的ModelValidator就是这么一个对象。从如下所以的成员定义代码并不能看出CompositeModelValidator有何特别之处。

    1:   private   class  CompositeModelValidator : ModelValidator
    2:  {
    3:       public  CompositeModelValidator(ModelMetadata metadata, ControllerContext controllerContext);
    4:       public   override  IEnumerable<ModelValidationResult> Validate( object  container);
    5:  }

从其类型名称可以看出CompositeModelValidator实际上并不是一个真正对Model对象实施验证的ModelValidator, 它是一系列ModelValidator的组合 ,它根据基于Model本身类型及其属性的Model元数据动态地获取相应的ModelValidator(通过调用ModelMetadata的GetValidators方法)对Model对象实施验证。

定义在Validate方法中的验证逻辑是这样的:CompositeModelValidator通过在构造函数中初始化的表示验证对象类型的Model元数据的ModelMetadata对象的Properties属性得到基于属性的Model元数据列表。然后遍历该列表的每个ModelMetadata对象,调用其GetValidators方法得到一组用于验证属性值得ModelValidator列表,然后使用该ModelValidator列表依次对相应的属性值进行验证,验证失败得到的ModelValidationResult对象被添加到最终返回的ModelValidationResult集合中。

只有在所有属性值都通过验证的情况下,CompositeModelValidator采用调用基于被验证类型Model元数据的ModelMetadata对象的GetValidators方法得到在类型级别ModelValidator列表对指定的数据对象实施验证,验证失败得到的ModelValidationResult对象被添加到最终返回的ModelValidationResult集合中。

抽象类ModelValidator具有一个静态的GetModelValidator方法根据指定的Model元数据和Controller上下文得到相应的ModelValidator对象。如下面的代码片断所示,该方法返回的正是一个CompositeModelValidator对象。

    1:   public   abstract   class  ModelValidator
    2:  {
    3:       //其他成员 
    4:       public   static  ModelValidator GetModelValidator(ModelMetadata metadata, ControllerContext context) 
    5:      {
    6:           return   new  CompositeModelValidator(metadata, context);
    7:      }
    8:  }


四、实例演示:探测CompositeModelValidator采用的验证行为

为了使读者对CompositeModelValidator的验证逻辑具有一个深刻的理解,我们来演示一个具体的Model验证的实例。我们创建了如果一个名称为AlwaysFailsAttribute的验证特性。如下面的代码片断所示,重写的IsValid方法总是返回False,意味着针对数据的验证总是会失败。我们还重写了只读属性TypeId,让它能够真正能够唯一标识一个AlwaysFailsAttribute特性实例,具体原因我们会在本章后续部分讲述。

    1:  [AttributeUsage( AttributeTargets.Class| AttributeTargets.Property)]
    2:    public   class  AlwaysFailsAttribute : ValidationAttribute
    3:  {
    4:       private   object  typeId;
    5:       public   override   bool  IsValid( object   value )
    6:      {
    7:           return   false ;
    8:      }
    9:       public   override   object  TypeId
   10:      {
   11:          get {  return  typeId ?? (typeId =  new   object ()); }
   12:      }
   13:  }

我们将AlwaysFailsAttribute应用到具有如下定义的表示联系人的Contact类型上。如下面的代码片断所示,我们在Contact和Address的类型和属性都应用了该特性,并且指定了相应的错误消息。

    1:  [AlwaysFails(ErrorMessage =  "Contact" )]
    2:   public   class  Contact
    3:  {
    4:      [AlwaysFails(ErrorMessage =  "Contact.Name" )]
    5:       public   string  Name { get; set; }
    6:   
    7:      [AlwaysFails(ErrorMessage =  "Contact.PhoneNo" )]
    8:       public   string  PhoneNo { get; set; }
    9:   
   10:      [AlwaysFails(ErrorMessage =  "Contact.EmailAddress" )]
   11:       public   string  EmailAddress { get; set; }
   12:   
   13:      [AlwaysFails(ErrorMessage =  "Contact.Address" )]
   14:       public  Address Address { get; set; }
   15:  }
   16:   
   17:  [AlwaysFails(ErrorMessage =  "Address" )]
   18:   public   class  Address
   19:  {
   20:      [AlwaysFails(ErrorMessage =  "Address.Province" )]
   21:       public   string  Province { get; set; }
   22:   
   23:      [AlwaysFails(ErrorMessage =  "Address.City" )]
   24:       public   string  City { get; set; }
   25:   
   26:      [AlwaysFails(ErrorMessage =  "Address.District" )]
   27:       public   string  District { get; set; }
   28:   
   29:      [AlwaysFails(ErrorMessage =  "Address.Street" )]
   30:       public   string  Street { get; set; }
   31:  }

在一个通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中,我们创建了具有如下定义的默认HomeController类。在Action方法Index中,我们使用当前的ModelMetadataProvider创建了基于Contact类型的ModelMetadata,然后调用ModelValidator的静态方法GetValidator方法得到基于该ModelMetadata和ControllerContext的ModelValidator对象(一个CompositeModelValidator对象)。最后我们通过该ModelValidator对象来验证手工创建的Contact对象,并将得到的ModelValidationResult对象的MemberName和Message属性呈现出来。

    1:   public   class  HomeController : Controller
    2:  {
    3:       public   void  Index()
    4:      {
    5:          Address address =  new  Address
    6:          {
    7:              Province =  "江苏" ,
    8:              City     =  "苏州" ,
    9:              District =  "工业园区" ,
   10:              Street   =  "星湖街328号" 
   11:          };
   12:          Contact contact =  new  Contact
   13:          {
   14:              Name         =  "张三" ,
   15:              PhoneNo      =  "123456789" ,
   16:              EmailAddress =  "zhangsan@gmail.com" ,
   17:              Address      = address
   18:          };
   19:   
   20:          ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => contact,  typeof (Contact));
   21:          ModelValidator validator = ModelValidator.GetModelValidator(metadata, ControllerContext);
   22:           foreach  (ModelValidationResult result  in  validator.Validate(contact))
   23:          { 
   24:              Response.Write( string .Format( "{0}: {1}<br/>" ,  string .IsNullOrEmpty(result.MemberName)?  "N/A" : result.MemberName, result.Message));
   25:          }
   26:      }
   27:  }

运行该程序后会在浏览器中得到如下所示的输出结果。这样的输出结果至少反映了两个问题,其一, CompositeModelValidator对数据的验证并不是递归进行的 ,因为只有应用在Contact属性上的验证特性参与了验证,而应用在Address类型属性上的验证特性则没有被使用;其二, 在属性认证失败的情况下是不会进行基于类型的验证的 ,因为浏览器中并不存在应用在Contact类型上的验证特性对应的输出。

    1:  Name        : Contact.Name
    2:  PhoneNo     : Contact.PhoneNo
    3:  EmailAddress: Contact.EmailAddress
    4:  Address     : Contact.Address
    5:  Address     : Address

上面的输出结果还反映了另外一个细节,针对某个属性的ModelValidator列表会同时包含应用在属性和属性对应类型的验证特性生成的ModelValidator。输出的最后两个ModelValidationResult都是针对Contact的Address属性的,分别对应着应用在Contact的Address属性和Address类型上的两个AlwaysFailsAttribute特性。现在我们按照如下的方式将应用在Contact的四个属性以及Address类型上的AlwaysFailsAttribute特性注册掉,只保留应用在Contact类型的AlwaysFailsAttribute特性。

    1:  [AlwaysFails(ErrorMessage =  "Contact" )]
    2:   public   class  Contact
    3:  {
    4:       //[AlwaysFails(ErrorMessage = "Contact.Name")] 
    5:       public   string  Name { get; set; }
    6:   
    7:       //[AlwaysFails(ErrorMessage = "Contact.PhoneNo")] 
    8:       public   string  PhoneNo { get; set; }
    9:   
   10:       //[AlwaysFails(ErrorMessage = "Contact.EmailAddress")] 
   11:       public   string  EmailAddress { get; set; }
   12:   
   13:       //[AlwaysFails(ErrorMessage = "Contact.Address")] 
   14:       public  Address Address { get; set; }
   15:  }
   16:   
   17:   //[AlwaysFails(ErrorMessage = "Address")] 
   18:   public   class  Address
   19:  {
   20:       //省略成员 
   21:  }

在此运行我们的程序将会在浏览器中得到如下的输出结果。不难看出输出的ModelValidationResult对应于着应用在Contact类型上的AlwaysFailsAttribute特性,这充分反映了上面所说的: 基于类型的验证只有在基于属性的验证失败的情况下才会进行 。

    1:  N/A: Contact

ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelValidator  
ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelValidatorProvider  
ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelValidatorProviders

作者: Artech
出处: http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelVali的详细内容...

  阅读:47次