ASP.NET MVC5学习笔记之Controller执行ControllerDescriptor和ActionDescriptor
一. ControllerDescriptor说明
ControllerDescriptor是一个抽象类,它定义的接口代码如下:
public abstract class ControllerDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable { public abstract ActionDescriptor FindAction(ControllerContext controllerContext, string actionName); public virtual object[] GetCustomAttributes(bool inherit); public virtual object[] GetCustomAttributes(Type attributeType, bool inherit); public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache); public virtual bool IsDefined(Type attributeType, bool inherit); }
从中我们看到包含Controller的一些基本信息,包括Controller的名字,类型,并实现了ICustomAttributeProvider接口,方便在其上查找应用的attribute, 其中更重要是定义一个抽象的FindAction方法,帮助确定在Controller上调用的是那一个Action。在ActionInvoker的FindAction方法其实是通过ControllerDescriptor的FindAction来得到ActionDescriptor。现在我们来看一下ControllerDescriptor的子类,如下图所示:
这里我们还是以同步的版本ReflectedControllerDescriptor为主,看看其FindAction方法的实现:
1 public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName) 2 { 3 //省略非相关代码 4 5 MethodInfo matched = _selector.FindActionMethod(controllerContext, actionName); 6 if (matched == null) 7 { 8 return null; 9 } 10 11 return new ReflectedActionDescriptor(matched, actionName, this); 12 }
可以看到,通一个名字_selector的属性的FindActionMethod方法查找Action的方法元数据,如果有找到方法元数据描述最终返回ReflectedActionDescriptor,否则返回null
_selector的类型名为ActionMethodSelector的内部类,它继承自ActionMethodSelectorBase类型, ActionMethodSelectorBase在初始化时会调用PopulateLookupTables方法,它会准备好在当前Controller上Action方法有关的数据, 主要包括三个方面的列表:
1. 在Action上应用了别名属性ActionNameSelectorAttribute的方法MethodInfo列表(AliasedMethods), ActionNameSelectorAttribute其作用在于允许请求的url中的Action name为ActionNameSelectorAttribute指定的name, 可以匹配该Action;
2. 正常的Action方法MethodInfo列表(NonAliasedMethods)
3. 在Action上应用了属性路由(Attribute Routing)MethodInfo列表
现在来看ActionMethodSelectorBase的FindActionMethod方法,具体代码如下:
1 public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName) 2 { 3 //省略非相关代码 4 5 List<MethodInfo> finalMethods = FindActionMethods(controllerContext, actionName); 6 7 switch (finalMethods.Count) 8 { 9 case 0: 10 return null; 11 12 case 1: 13 return finalMethods[0]; 14 15 default: 16 throw CreateAmbiguousActionMatchException(finalMethods, actionName); 17 } 18 }
可以看到,查找动作又委托给了其内部的FindActionMethods方法:
1 protected List<MethodInfo> FindActionMethods(ControllerContext controllerContext, string actionName) 2 { 3 List<MethodInfo> matches = new List<MethodInfo>(); 4 5 // Performance sensitive, so avoid foreach 6 for (int i = 0; i < AliasedMethods.Length; i++) 7 { 8 MethodInfo method = AliasedMethods[i]; 9 if (IsMatchingAliasedMethod(method, controllerContext, actionName)) 10 { 11 matches.Add(method); 12 } 13 } 14 matches.AddRange(NonAliasedMethods[actionName]); 15 RunSelectionFilters(controllerContext, matches); 16 return matches; 17 }
FindActionMethods首先检查AliasedMethods中是否有方法与当前的action的name匹配,如果匹配则把当前的MethodInfo加入返回列表; 接着在NonAliasedMethods根据action name查找MethodInfo并加入返回列表,最后调用RunSelectionFilters对查找到的方法进行筛选。它的代码如下:
1 protected static void RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos) 2 { 3 // Filter depending on the selection attribute. 4 // Methods with valid selection attributes override all others. 5 // Methods with one or more invalid selection attributes are removed. 6 7 bool hasValidSelectionAttributes = false; 8 // loop backwards for fastest removal 9 for (int i = methodInfos.Count - 1; i >= 0; i--) 10 { 11 MethodInfo methodInfo = methodInfos[i]; 12 ReadOnlyCollection<ActionMethodSelectorAttribute> attrs = ReflectedAttributeCache.GetActionMethodSelectorAttributesCollection(methodInfo); 13 if (attrs.Count == 0) 14 { 15 // case 1: this method does not have a MethodSelectionAttribute 16 17 if (hasValidSelectionAttributes) 18 { 19 // if there is already method with a valid selection attribute, remove method without one 20 methodInfos.RemoveAt(i); 21 } 22 } 23 else if (IsValidMethodSelector(attrs, controllerContext, methodInfo)) 24 { 25 // case 2: this method has MethodSelectionAttributes that are all valid 26 27 // if a matching action method had a selection attribute, consider it more specific than a matching action method 28 // without a selection attribute 29 if (!hasValidSelectionAttributes) 30 { 31 // when the first selection attribute is discovered, remove any items later in the list without selection attributes 32 if (i + 1 < methodInfos.Count) 33 { 34 methodInfos.RemoveFrom(i + 1); 35 } 36 hasValidSelectionAttributes = true; 37 } 38 } 39 else 40 { 41 // case 3: this method has a method selection attribute but it is not valid 42 43 // remove the method since it is opting out of this request 44 methodInfos.RemoveAt(i); 45 } 46 } 47 }
RunSelectionFilters方法是检查Action应用的ActionMethodSelectorAttribute规则, 以确定最终的匹配的Action MethodInfo。
ActionMethodSelectorAttribute一个抽象类,只定义了一个抽象方法:
public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo),用来检查在当前请求中,Action是否允许执行。比如在Action上声明了HttpPostAttribute,则只允许当前的http是POST请求,才允许执行当前Action.ActionMethodSelectorAttribute有很多子类,如下所示:
1 // 摘要: 2 // 包含描述反射的操作方法的信息。 3 public class ReflectedActionDescriptor : ActionDescriptor 4 { 5 // 摘要: 6 // 初始化 System.Web.Mvc.ReflectedActionDescriptor 类的新实例。 7 // 8 // 参数: 9 // methodInfo: 10 // 操作方法信息。 11 // 12 // actionName: 13 // 操作的名称。 14 // 15 // controllerDescriptor: 16 // 控制器描述符。 17 // 18 // 异常: 19 // System.ArgumentNullException: 20 // methodInfo 或 controllerDescriptor 参数为 null。 21 // 22 // System.ArgumentException: 23 // actionName 参数为 null 或为空。 24 public ReflectedActionDescriptor(MethodInfo methodInfo, string actionName, ControllerDescriptor controllerDescriptor); 25 26 // 摘要: 27 // 获取操作的名称。 28 // 29 // 返回结果: 30 // 操作的名称。 31 public override string ActionName { get; } 32 // 33 // 摘要: 34 // 获取控制器描述符。 35 // 36 // 返回结果: 37 // 控制器描述符。 38 public override ControllerDescriptor ControllerDescriptor { get; } 39 // 40 // 摘要: 41 // 获取或设置操作方法信息。 42 // 43 // 返回结果: 44 // 操作方法信息。 45 public MethodInfo MethodInfo { get; } 46 // 47 // 摘要: 48 // 使用延迟初始化来获取反射的操作描述符的唯一 ID。 49 // 50 // 返回结果: 51 // 唯一 ID。 52 public override string UniqueId { get; } 53 54 // 摘要: 55 // 使用指定的操作方法参数来执行指定的控制器上下文。 56 // 57 // 参数: 58 // controllerContext: 59 // 控制器上下文。 60 // 61 // parameters: 62 // 参数。 63 // 64 // 返回结果: 65 // 操作返回值。 66 // 67 // 异常: 68 // System.ArgumentNullException: 69 // parameters 或 controllerContext 参数为 null。 70 public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters); 71 // 72 // 摘要: 73 // 返回为此成员定义的自定义特性的数组,指定的特性除外。 74 // 75 // 参数: 76 // inherit: 77 // 要查找继承的自定义特性的层次结构链,则为 true;否则为 false。 78 // 79 // 返回结果: 80 // 自定义特性的数组,如果没有自定义特性,则为空数组。 81 // 82 // 异常: 83 // System.TypeLoadException: 84 // 无法加载自定义特性类型。 85 // 86 // System.Reflection.AmbiguousMatchException: 87 // 为此成员定义的 attributeType 类型特性不止一个。 88 public override object[] GetCustomAttributes(bool inherit); 89 // 90 // 摘要: 91 // 返回为此成员定义的自定义特性的数组(按类型标识)。 92 // 93 // 参数: 94 // attributeType: 95 // 自定义特性的类型。 96 // 97 // inherit: 98 // 要查找继承的自定义特性的层次结构链,则为 true;否则为 false。 99 // 100 // 返回结果: 101 // 自定义特性的数组,如果没有自定义特性,则为空数组。 102 // 103 // 异常: 104 // System.TypeLoadException: 105 // 无法加载自定义特性类型。 106 // 107 // System.Reflection.AmbiguousMatchException: 108 // 为此成员定义的 attributeType 类型特性不止一个。 109 public override object[] GetCustomAttributes(Type attributeType, bool inherit); 110 // 111 // 摘要: 112 // 获取筛选器特性。 113 // 114 // 参数: 115 // useCache: 116 // 若要使用缓存,则为 true,否则为 false。 117 // 118 // 返回结果: 119 // 筛选器特性。 120 public override IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache); 121 // 122 // 摘要: 123 // 检索操作方法的参数。 124 // 125 // 返回结果: 126 // 操作方法的参数。 127 public override ParameterDescriptor[] GetParameters(); 128 // 129 // 摘要: 130 // 检索操作选择器。 131 // 132 // 返回结果: 133 // 操作选择器。 134 public override ICollection<ActionSelector> GetSelectors(); 135 // 136 // 摘要: 137 // 指示是否为此成员定义某个自定义特性类型的一个或多个实例。 138 // 139 // 参数: 140 // attributeType: 141 // 自定义特性的类型。 142 // 143 // inherit: 144 // 要查找继承的自定义特性的层次结构链,则为 true;否则为 false。 145 // 146 // 返回结果: 147 // 如果为此成员定义了自定义特性类型,则为 true;否则为 false。 148 public override bool IsDefined(Type attributeType, bool inherit); 149 }
我们看到它继承自ActionDescriptor,整个ActionDescriptor的继承关系如下所示: