zoukankan      html  css  js  c++  java
  • mvc源码解读(15)数据绑定组件ModelBinder之ModelBinderAttribute

           我们继续我们MVC3源码的分析,我们在讲到MVC的过滤器的之前,我们讲到了Controller,Action和Parameter对应的描述对象ControllerDescriptor,ActionDescriptor,ParameterDescriptor,这三个对象中包含着控制器,方法,方法参数的一些相关信息,我们分析到类ControllerActionInvoker里面的InvokeAction方法里面的这一句代码:

     //执行数据的绑定(参数的初始化)
    IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);

    我们上次在介绍ParameterDescriptor的时候有说到,GetParameterValues方法里面最终又调用了GetParameterValue方法并返回由GetParameterValue返回的数组对象parametersDict,因此我们来看GetParameterValue的具体实现:

     //真正数据绑定的核心        

    protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {                       

    Type parameterType = parameterDescriptor.ParameterType;            

    //获取binder对象            

    IModelBinder binder = GetModelBinder(parameterDescriptor);            

    //获取Action方法参数值            

    IValueProvider valueProvider = controllerContext.Controller.ValueProvider;            

    //获取Action参数名(看这个参数是否有前缀名(用于复杂类型),没有的话直接返回参数名)            

    string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;

    Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);       

    ModelBindingContext bindingContext = new ModelBindingContext() {                

             FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null),

             //获得模型元数据。                

             ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),                

             ModelName = parameterName,                

             //这里有验证的逻辑                

             ModelState = controllerContext.Controller.ViewData.ModelState,                

             PropertyFilter = propertyFilter,                

             ValueProvider = valueProvider            

        };

    object result = binder.BindModel(controllerContext, bindingContext);            

    return result ?? parameterDescriptor.DefaultValue;        

    }

     我们看红色的代码,GetModelBinder方法是获取到ModelBinder的对象,我们在获取这个对象的过程可以叫做Model的绑定过程,具体来说所谓Model的绑定就是从客户端发来的请求中获取到相应的Action方法参数的一个过程,而这个过程是由一个叫ModelBinders的静态类来实现的。Model的绑定有中方式,我们先来看第一种情况,如下面GetModelBinder方法所示:

    private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) {
                // look on the parameter itself, then look in the global table
                return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
            }

    当parameterDescriptor.BindingInfo.Binder不为空的时候,返回的是类ModelBinder里面的GetBinderFromAttributesImpl方法的返回值,具体实现如下:

     private static IModelBinder GetBinderFromAttributesImpl(CustomModelBinderAttribute[] attrs, Func<string> errorMessageAccessor) {            

                if (attrs == null) {return null; }

                switch (attrs.Length) {                

                    case 0:return null;

                    case 1: IModelBinder binder = attrs[0].GetBinder();

                                return binder;

                    default:string errorMessage = errorMessageAccessor();

                                throw new InvalidOperationException(errorMessage);            

                 }        

    }

    这个方法什么时候返回的值不是为空呢?那就是当Action的方法参数上有CustomModelBinder或是ModelBinder(ModelBinderAttribute这个密封类继承自CustomModelBinderAttribute类)特性标注的时候。同时根据源码我们可以发现:Action方法中的参数只允许有一个CustomModelBinder标注特性,不能有多种类型CustomModelBinder存在。因此如果Action方法参数上有CustomModelBinder特性标注的话,就会优先选择这个Action参数的ParameterDescriptor的ModelBinder来绑定参数。ModelBinderAttribute继承了CustomModelBinderAttribute并重写了GetBinder方法,具体如下:

     public override IModelBinder GetBinder() {
                try {
                    return (IModelBinder)Activator.CreateInstance(BinderType);
                }
                catch (Exception ex) {
                    throw new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            MvcResources.ModelBinderAttribute_ErrorCreatingModelBinder,
                            BinderType.FullName),
                        ex);
                }
            }

    里面的GetBinder方法就是根据BinderType来创建了一个ModelBinder对象。当然还有FormCollectionModelBinder,SkipBindingAttribute,DeserializeAttribute都继承了CustomModelBinderAttribute并都重写了GetBinder()。我们来看FormCollectionModelBinder表单集合的ModelBinder:

     private sealed class FormCollectionBinderAttribute : CustomModelBinderAttribute {      

                private static readonly FormCollectionModelBinder _binder = new FormCollectionModelBinder();

                public override IModelBinder GetBinder() {return _binder;} 

      private sealed class FormCollectionModelBinder : IModelBinder {

                         public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {                    

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

                                   return new FormCollection(controllerContext.Controller, () => controllerContext.HttpContext.Request.Form, () => controllerContext.HttpContext.Request.Unvalidated().Form);

                                   }            

                   }        

    }

    我们可以发现这个FormCollectionModelBinder就是从表单Form中获取到相应的Action方法的参数。

          我们来看DeserializeAttribute这个类里面的实现:

     public override IModelBinder GetBinder() {
                return new DeserializingModelBinder(Mode, Serializer);
            } 

    private sealed class DeserializingModelBinder : IModelBinder {

               private readonly SerializationMode _mode;            

               private readonly MvcSerializer _serializer;

               public DeserializingModelBinder(SerializationMode mode, MvcSerializer serializer) {_mode = mode;_serializer = serializer ?? new MvcSerializer();}

               public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {

                         ValueProviderResult vpResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);                

                         if (vpResult == null) { return null;}

                         string serializedValue = (string)vpResult.ConvertTo(typeof(string));                

                         return _serializer.Deserialize(serializedValue, _mode);}

            }

    这个DeserializingModelBinder就是通过反系列化之后获取到Action参数的ModelBinder。这样第一种的ModelBinder我们就获取到了。

  • 相关阅读:
    Vue异步数据交互 promise axios async fetch
    JS数组或对象转为JSON字符换 JavaScript JSON.stringify()
    JS返回数组的最大值与最小值
    Error: Cannot find module './application'
    Express框架
    NodeJS项目制作流程
    模板引擎art-template
    基于NodeJS的网络响应原理与HTTP协议
    leetcode 953. 验证外星语词典 做题笔记
    leetcode 771. 宝石与石头 做题笔记
  • 原文地址:https://www.cnblogs.com/ghhlyy/p/2939045.html
Copyright © 2011-2022 走看看