我们继续我们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() { 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我们就获取到了。