好得很程序员自学网

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

深入ASP.NET MVC之:服务器端Model Validation

深入ASP.NET MVC之:服务器端Model Validation

ASP.NET MVC 3支持两大类型的验证:服务端和客户端脚本验证。本文先介绍服务端验证。在前文也介绍过,服务器端的验证是发生在模型绑定的时候,在DefaultModelBinder中有如下方法会触发验证:

 internal void   BindComplexElementalModel(  ControllerContext   controllerContext,   ModelBindingContext   bindingContext,   object   model) {
              // need to replace the property filter + model object and create an inner binding context
              ModelBindingContext   newBindingContext = CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model);

              // validation
              if   (OnModelUpdating(controllerContext, newBindingContext)) {
                BindProperties(controllerContext, newBindingContext);
                OnModelUpdated(controllerContext, newBindingContext);
            } 

其中,OnModelUpdated方法是真正触发验证逻辑的地方:

 protected virtual void   OnModelUpdated(  ControllerContext   controllerContext,   ModelBindingContext   bindingContext) {
              Dictionary  <  string  ,   bool  > startedValid =   new   Dictionary  <  string  ,   bool  >(  StringComparer  .OrdinalIgnoreCase);
              var   res =   ModelValidator  .GetModelValidator(bindingContext.ModelMetadata, controllerContext).Validate(  null  );
              foreach   (  ModelValidationResult   validationResult   in   res) {
                  string   subPropertyName = CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName);

                  if   (!startedValid.ContainsKey(subPropertyName)) {
                    startedValid[subPropertyName] = bindingContext.ModelState.IsValidField(subPropertyName);
                }

                  if   (startedValid[subPropertyName]) {
                    bindingContext.ModelState.AddModelError(subPropertyName, validationResult.Message);
                }
            }
        } 

首先,它获得一个ModelValidator,这个ModelValidator是一个CompositeModelValidator,然后调用其Validate方法:

 public override   IEnumerable  <  ModelValidationResult  > Validate(  object   container) {
                  bool   propertiesValid =   true  ;

                  foreach   (  ModelMetadata   propertyMetadata   in   Metadata.Properties) {
                      foreach   (  ModelValidator   propertyValidator   in   propertyMetadata.GetValidators(ControllerContext)) {
                          foreach   (  ModelValidationResult   propertyResult   in   propertyValidator.Validate(Metadata.Model)) {
                            propertiesValid =   false  ;
                              yield return new   ModelValidationResult   {
                                MemberName =   DefaultModelBinder  .CreateSubPropertyName(propertyMetadata.PropertyName, propertyResult.MemberName),
                                Message = propertyResult.Message
                            };
                        }
                    }
                }

                  if   (propertiesValid) {
                      var   typeValidators = Metadata.GetValidators(ControllerContext);
                      foreach   (  ModelValidator   typeValidator   in   typeValidators)
                    {
                          foreach   (  ModelValidationResult   typeResult   in   typeValidator.Validate(container)) {
                              yield return   typeResult;
                        }
                    }
                }
            } 

首先是查找model的property上的attribute,然后将其转换成一个IModelValidator对象,这里使用的是适配器模式,看下GetValidators方法,这是ModelMetaData的方法:

 public virtual   IEnumerable  <  ModelValidator  > GetValidators(  ControllerContext   context) {
              return   ModelValidatorProviders  .Providers.GetValidators(  this  , context);
        } 

ModelValidatorProviders定义如下:

 public static class   ModelValidatorProviders   {

          private static readonly   ModelValidatorProviderCollection   _providers =   new   ModelValidatorProviderCollection  () {
              new   DataAnnotationsModelValidatorProvider  (),
              new   DataErrorInfoModelValidatorProvider  (),
              new   ClientDataTypeModelValidatorProvider  ()
        };

          public static   ModelValidatorProviderCollection   Providers {
              get   {
                  return   _providers;
            }
        }
    } 

用的最多的是 DataAnnotationsModelValidatorProvider ,他是继承自AssociatedValidatorProvider, 看下它的GetValidators方法:

 public override sealed   IEnumerable  <  ModelValidator  > GetValidators(  ModelMetadata   metadata,   ControllerContext   context) {
   if   (metadata.ContainerType !=   null   && !  String  .IsNullOrEmpty(metadata.PropertyName)) {
                  return   GetValidatorsForProperty(metadata, context);
            }
              return   GetValidatorsForType(metadata, context);
        } 

首先判断当前的metadata是model的一个属性,还是model对象本身,分别调用两个不同的方法去获得model validator,这两个方法的不同之处只是在于通过反射分别获得放置在类型和属性(Proerty)上的属性(Attribute),然后调用抽象方法GetValidators,这个抽象方法在 DataAnnotationsModelValidatorProvider 中,实现如下

 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();
            }
        } 

首先是为不能为null的字段默认添加上required属性。然后遍历所有的标签中类型为ValidationAttribute的属性,对于每个属性,需要去找到一个 DataAnnotationsModelValidationFactory  ,这个factory是一个委托:

 public delegate   ModelValidator DataAnnotationsModelValidationFactory  (  ModelMetadata   metadata,   ControllerContext   context,   ValidationAttribute   attribute); 

这里可以认为是使用了Adapter模式,将通过属性的验证方法转换成ModelValidator。MVC针对各种不同的验证类型提供了一些对应的adapter factory(有删节):

 internal static   Dictionary  <  Type  ,   DataAnnotationsModelValidationFactory  > AttributeFactories =   new   Dictionary  <  Type  ,   DataAnnotationsModelValidationFactory  >() {
            {
                  typeof  (  RangeAttribute  ),
                (metadata, context, attribute) =>   new   RangeAttributeAdapter  (metadata, context, (  RangeAttribute  )attribute)
            },
           //…
};

这些对应的adapter factory主要为了提供客户端验证的支持,下面谈到客户端验证的时候再介绍,其余的功能和DefaultAttributeFactory类似,

 internal static   DataAnnotationsModelValidationFactory   DefaultAttributeFactory =
            (metadata, context, attribute) =>   new   DataAnnotationsModelValidator  (metadata, context, attribute); 

DataAnnotaionsModelValidator的主要方法如下:

 public class   DataAnnotationsModelValidator   :   ModelValidator   {
          public   DataAnnotationsModelValidator(  ModelMetadata   metadata,   ControllerContext   context,   ValidationAttribute   attribute)
            :   base  (metadata, context) {

              if   (attribute ==   null  ) {
                  throw new   ArgumentNullException  (  "attribute"  );
            }

            Attribute = attribute;
        }

   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
                };
            }
        }
    } 

事实上,诸如 RangeAttributeAdapter 此类的类都继承自 DataAnnotationsModelValidator ,对于服务端的验证,Validate方法来说,都是一样的。验证的结果统一表示为ValidationResult对象,被放置在bindingContext.ModelState中。渲染页面的时候,就会从ModelState中取出错误消息,显示在页面上。通过以上分析,可以知道服务端验证的过程大致是,对于一个model,首先获得他每个属性上的validation attribute,调用其验证方法,如果此属性是 IValidatableObject 类型的,同样调用其验证方法;如果对象本身有validationattribute,或者本身是IValidateableObject,则同样调用其对应的验证方法。

 

 

分类:  Web开发

作者: Leo_wl

    

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

    

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

版权信息

查看更多关于深入ASP.NET MVC之:服务器端Model Validation的详细内容...

  阅读:43次