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);
    }
View Code

从中我们看到包含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         }    
View Code

可以看到,通一个名字_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列表

关于属性路由的概念请参考
http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx

现在来看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         }
View Code

可以看到,查找动作又委托给了其内部的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         }
View Code

 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         }
View Code

RunSelectionFilters方法是检查Action应用的ActionMethodSelectorAttribute规则, 以确定最终的匹配的Action MethodInfo。

ActionMethodSelectorAttribute一个抽象类,只定义了一个抽象方法:

 public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo),用来检查在当前请求中,Action是否允许执行。比如在Action上声明了HttpPostAttribute,则只允许当前的http是POST请求,才允许执行当前Action.ActionMethodSelectorAttribute有很多子类,如下所示:

除了NoActionAttribute ,其它的Attribute都是引用AcceptVerbsAttribute的实现,只不过提供一种简洁的使用方式。通过了RunSelectionFilters筛选,返回最终Action MethodInfo列表,在ActionMethodSelectorBase的FindActionMethod方法检查返回结果,如果返回MethodInfo数量为0,返回null, 为1是正常状态,大于1抛出AmbiguousMatchException.
 
最终返回到ReflectedControllerDescriptor的FindAction方法中,实例化ReflectedActionDescriptor并返回。
 
二. ReflectedActionDescriptor的说明
它的接口如下:
  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     }
View Code

我们看到它继承自ActionDescriptor,整个ActionDescriptor的继承关系如下所示:

  

ASP.NET MVC5学习笔记之Controller执行ControllerDescriptor和ActionDescriptor,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。