mvc源码解读(10)-ParameterDescriptor方法Action方法的参数描述对象
前面两篇文章中我们已经大概的讲解了一下ControllerDescriptor和ActionDescriptor这两个分别对应Controller和Action的描述对象,已经大概的知道他们包含的信息了。这篇文章我们将根据mvc3的源码继续往下看,在ControllerActionInvoker类中的InvokeAction方法中通过FindAction获取到ActionDescriptor之后,直接将代码贴上来吧:
跳过中间的那些代码(稍后补充上),直接看这一句:
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor); |
GetParameterValues方法具体实现如下:
//数据参数绑定包装类 protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters(); foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors) { parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor); } return parametersDict; } |
我们可以看到红色代码中GetParameters方法就是返回一个ParameterDescriptor对象,而GetParameters我们在讲ActionDescriptor的时候就已经说了一下,它最终调用的是ReflectedActionDescriptor里面的LazilyFetchParametersCollection方法:
private ParameterDescriptor[] LazilyFetchParametersCollection() { return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>( ref _parametersCache /* cacheLocation */, MethodInfo.GetParameters /* initializer */, parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */); } |
ReflectedParameterDescriptor的构造函数如下:
public ReflectedParameterDescriptor(ParameterInfo parameterInfo, ActionDescriptor actionDescriptor) { ParameterInfo = parameterInfo; _actionDescriptor = actionDescriptor; _bindingInfo = new ReflectedParameterBindingInfo(parameterInfo); } |
这里面同时又涉及到一个ReflectedParameterBindingInfo类,该类的构造函数如下:
public ReflectedParameterBindingInfo(ParameterInfo parameterInfo) { _parameterInfo = parameterInfo; ReadSettingsFromBindAttribute(); } |
ReadSettingsFromBindAttribute方法如下:
private void ReadSettingsFromBindAttribute() { BindAttribute attr = (BindAttribute)Attribute.GetCustomAttribute(_parameterInfo, typeof(BindAttribute)); if (attr == null) {return;} _exclude = new ReadOnlyCollection<string>(AuthorizeAttribute.SplitString(attr.Exclude)); _include = new ReadOnlyCollection<string>(AuthorizeAttribute.SplitString(attr.Include)); _prefix = attr.Prefix; } |
这里涉及到的密封类BindAttribute继承自Attribute,顾名思义就是获取应用在方法参数上的自定义特性。BindAttribute的有如下成员:
public string Exclude { get {return _exclude ?? String.Empty; } set {_exclude = value; _excludeSplit = AuthorizeAttribute.SplitString(value); } } public string Include { get {return _include ?? String.Empty; } set {_include = value;_includeSplit = AuthorizeAttribute.SplitString(value); } } public string Prefix {get;set;} internal static bool IsPropertyAllowed(string propertyName, string[] includeProperties, string[] excludeProperties) { bool includeProperty = (includeProperties == null) || (includeProperties.Length == 0) || includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase); bool excludeProperty = (excludeProperties != null) && excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase); return includeProperty && !excludeProperty; } } |
IsPropertyAllowed方法表示指定的属性是否要用于绑定。因此我们回到ReflectedParameterDescriptor这个类的BindingInfo属性:
private readonly ReflectedParameterBindingInfo _bindingInfo; public override ParameterBindingInfo BindingInfo { |
这样我们可以得知ReflectedParameterDescriptor的BindingInfo的Include、Exclude和Prefix属性对应的就是BindAttribute里面的相关属性。分别表示是否显示自己设置的绑定,不绑定的属性名称,Prefix表示绑定的参数属性的前缀名称。因为参数绑时在请求中数据都是带有类名作为前缀的。再回到上面的ReflectedParameterBindingInfo集成自抽象类ParameterBindingInfo,该类有如下成员:
public abstract class ParameterBindingInfo { public virtual IModelBinder Binder {get {return null;}} public virtual ICollection<string> Exclude {get {return new string[0];}} public virtual ICollection<string> Include {get {return new string[0];}} public virtual string Prefix {get {return null; }} |
属性Exclude,Include和Prefix都被子类ReflectedParameterBindingInfo重写了,我们主要看红色的代码,接口IModelBinder的定义如下:
public interface IModelBinder { object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext); } |
接口里面的BindModel方法就是整个mvc的模型绑定的核心,通过以上分析我们可以知道,无论是ControllerDescriptor,ActionDescriptor还是ParameterDescriptor都是为Model绑定获取前提条件,这个后面我们将详细介绍。