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的子类,如下图所示:

ASP.NET MVC5学习笔记之Controller执行ControllerDescriptor和ActionDescriptor

这里我们还是以同步的版本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有很多子类,如下所示:

ASP.NET MVC5学习笔记之Controller执行ControllerDescriptor和ActionDescriptor

除了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

你可能感兴趣的:(controller)