• ASP.NET MVC5学习笔记之Action参数模型绑定基本过程


      当我们在Controller中定义一个Action,通常会定义一个或多个参数,每个参数称为一个模型,ASP.NET MVC框架提供了一种机制称为模型绑定,会尝试自动从请求的信息中实例化每一个模型并赋值。这其中又涉及模型的元数据提供和模型的验证。

      我们不妨试想一下,如果来定义一种从字符串值到对象值的映射机制,可能要知道以下信息:

    1. 对象的类型和名称等对象本身的元数据

    2. 对象属性的元数据信息

    3. 查询字符串到对象和对象属性的值映射机制

    4. 具体的绑定过程

      前面的1,2 由ASP.NET MVC框架的元数据提供机制保证,3由其值提供机制保证,4由具体的模型绑定对象执行。只不过ASP.NET MVC在做完模型绑定后,顺带验证了模型的验证。

      现在我们来看看具体的代码.参数模型绑定执行肯定在具体的Action方法执行之前,在ControllerActionInvoker的InvokeAction方中,执行Action方法是InvokeActionMethodWithFilters,在其前一行代码是IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor); 这个方法的目的把Action的所有参数值根据参数名封装到一个字典里。现在来看看其实现:

     1 protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
     2         {
     3             Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
     4             ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters();
     5 
     6             foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors)
     7             {
     8                 parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor);
     9             }
    10             return parametersDict;
    11         }

      可以看到通过ActionDescriptor获取所有的参数描述信息数组,每个参数通过调用GetParameterValue获取其参数值。这里有一个重要的类ParameterDescriptor,该类是个抽象类其定义如下:

     1  public abstract class ParameterDescriptor : ICustomAttributeProvider
     2     {
     3        
     4         protected ParameterDescriptor();
     5         public abstract ActionDescriptor ActionDescriptor { get; }
     6 
     7         public virtual ParameterBindingInfo BindingInfo { get; }
     8 
     9         public virtual object DefaultValue { get; }
    10         public abstract string ParameterName { get; }
    11         public abstract Type ParameterType { get; }
    12         public virtual object[] GetCustomAttributes(bool inherit);
    13         public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);
    14         public virtual bool IsDefined(Type attributeType, bool inherit);
    15     }

     这个类型除了BindingInfo属性其它属性没什么好说的,BindingInfo的类型是ParameterBindingInfo,描述该参数类型模型绑定过程中将使用的信息,具体那些信息我们来看这个类型的定义:

    1  public abstract class ParameterBindingInfo
    2     {
    3         protected ParameterBindingInfo();
    4         public virtual IModelBinder Binder { get; }
    5         public virtual ICollection<string> Exclude { get; }
    6         public virtual ICollection<string> Include { get; }
    7         public virtual string Prefix { get; }
    8     }

    Binder属性是指参数特定的绑定器,通过ModelBinderAttribute来指定, Exclude和Include表示不参与或参与参数属性模型绑定列表,Prefix 表示绑定过程中使用的前缀,这三个属性是通过BindAttribute属性来设置的。

      现在回过头来看看GetParameterValue 方法的实现:

     1 protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
     2         {
     3             // collect all of the necessary binding properties
     4             Type parameterType = parameterDescriptor.ParameterType;
     5             IModelBinder binder = GetModelBinder(parameterDescriptor);
     6             IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
     7             string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
     8             Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);
     9 
    10             // finally, call into the binder
    11             ModelBindingContext bindingContext = new ModelBindingContext()
    12             {
    13                 FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified
    14                 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
    15                 ModelName = parameterName,
    16                 ModelState = controllerContext.Controller.ViewData.ModelState,
    17                 PropertyFilter = propertyFilter,
    18                 ValueProvider = valueProvider
    19             };
    20 
    21             object result = binder.BindModel(controllerContext, bindingContext);
    22             return result ?? parameterDescriptor.DefaultValue;
    23         }

       a. 前面的几行代码准备绑定上下文使用的信息,简单的这里不说了,具体的ModelBinder通过GetModelBinder方法获取,它的实现是这样的:

      return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType); 

      我们可以知道,通过ModelBinderAttribute指定的ModelBinder具有最高的优先级,Binders是ModelBinders.Binders静态属性,如果参数未指定ModelBinder,则通过ModelBinders.Binders.ModelBinders.Binders.GetBinder方法查找,具体查找这里不分析了,这里给出一个结果:

      1. 在参数上应用了ModelBinderAttribute

      2. 在ModelBinderProvider集合中查找

      3. 在全局注册的模型绑定集合表中查找

      4. 在参数类型上通过CustomModelBinderAttribute指定的模型绑定类型

      5. 返回默认的DefaultModelBinder

     b. 实例化绑定上文下,我们看到如果我们通过BinderAttribute的Prefix指定了前缀,系统默认的FallbackToEmptyPrefix将不再使用, 另外参数模型的元数据通过调用系统的元数据提供机制ModelMetadataProviders.Current.GetMetadataForType.

      c. 执行模型绑定,如果返回null再返回参数指定的默认值

       这段代码信息量比较大,包含了元数据提供机制,值提供机制,模型绑定与验证。后面的章节将分别描述。

  • 相关阅读:
    技术部新年开工第一场会议
    mockjs学习总结(方便前端模拟数据,加快开发效率)
    vue开发后台管理系统小结
    网站代码、图片优化汇总
    前端seo小结,网页代码优化
    Java 并发编程之锁的实现
    python之格式化输出
    python七类之列表元组
    python七类之集合
    函数风云序列之函数初现
  • 原文地址:https://www.cnblogs.com/jjyjjyjjy/p/3726789.html
Copyright © 2020-2023  润新知