一. 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方法的实现:

 public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName)
{
//省略非相关代码 MethodInfo matched = _selector.FindActionMethod(controllerContext, actionName);
if (matched == null)
{
return null;
} return new ReflectedActionDescriptor(matched, actionName, this);
}

可以看到,通一个名字_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方法,具体代码如下:

  public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName)
{
//省略非相关代码 List<MethodInfo> finalMethods = FindActionMethods(controllerContext, actionName); switch (finalMethods.Count)
{
case :
return null; case :
return finalMethods[]; default:
throw CreateAmbiguousActionMatchException(finalMethods, actionName);
}
}

可以看到,查找动作又委托给了其内部的FindActionMethods方法:

  protected List<MethodInfo> FindActionMethods(ControllerContext controllerContext, string actionName)
{
List<MethodInfo> matches = new List<MethodInfo>(); // Performance sensitive, so avoid foreach
for (int i = ; i < AliasedMethods.Length; i++)
{
MethodInfo method = AliasedMethods[i];
if (IsMatchingAliasedMethod(method, controllerContext, actionName))
{
matches.Add(method);
}
}
matches.AddRange(NonAliasedMethods[actionName]);
RunSelectionFilters(controllerContext, matches);
return matches;
}

FindActionMethods首先检查AliasedMethods中是否有方法与当前的action的name匹配,如果匹配则把当前的MethodInfo加入返回列表; 接着在NonAliasedMethods根据action name查找MethodInfo并加入返回列表,最后调用RunSelectionFilters对查找到的方法进行筛选。它的代码如下:

  protected static void RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos)
{
// Filter depending on the selection attribute.
// Methods with valid selection attributes override all others.
// Methods with one or more invalid selection attributes are removed. bool hasValidSelectionAttributes = false;
// loop backwards for fastest removal
for (int i = methodInfos.Count - ; i >= ; i--)
{
MethodInfo methodInfo = methodInfos[i];
ReadOnlyCollection<ActionMethodSelectorAttribute> attrs = ReflectedAttributeCache.GetActionMethodSelectorAttributesCollection(methodInfo);
if (attrs.Count == )
{
// case 1: this method does not have a MethodSelectionAttribute if (hasValidSelectionAttributes)
{
// if there is already method with a valid selection attribute, remove method without one
methodInfos.RemoveAt(i);
}
}
else if (IsValidMethodSelector(attrs, controllerContext, methodInfo))
{
// case 2: this method has MethodSelectionAttributes that are all valid // if a matching action method had a selection attribute, consider it more specific than a matching action method
// without a selection attribute
if (!hasValidSelectionAttributes)
{
// when the first selection attribute is discovered, remove any items later in the list without selection attributes
if (i + < methodInfos.Count)
{
methodInfos.RemoveFrom(i + );
}
hasValidSelectionAttributes = true;
}
}
else
{
// case 3: this method has a method selection attribute but it is not valid // remove the method since it is opting out of this request
methodInfos.RemoveAt(i);
}
}
}

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的说明
它的接口如下:
  // 摘要:
// 包含描述反射的操作方法的信息。
public class ReflectedActionDescriptor : ActionDescriptor
{
// 摘要:
// 初始化 System.Web.Mvc.ReflectedActionDescriptor 类的新实例。
//
// 参数:
// methodInfo:
// 操作方法信息。
//
// actionName:
// 操作的名称。
//
// controllerDescriptor:
// 控制器描述符。
//
// 异常:
// System.ArgumentNullException:
// methodInfo 或 controllerDescriptor 参数为 null。
//
// System.ArgumentException:
// actionName 参数为 null 或为空。
public ReflectedActionDescriptor(MethodInfo methodInfo, string actionName, ControllerDescriptor controllerDescriptor); // 摘要:
// 获取操作的名称。
//
// 返回结果:
// 操作的名称。
public override string ActionName { get; }
//
// 摘要:
// 获取控制器描述符。
//
// 返回结果:
// 控制器描述符。
public override ControllerDescriptor ControllerDescriptor { get; }
//
// 摘要:
// 获取或设置操作方法信息。
//
// 返回结果:
// 操作方法信息。
public MethodInfo MethodInfo { get; }
//
// 摘要:
// 使用延迟初始化来获取反射的操作描述符的唯一 ID。
//
// 返回结果:
// 唯一 ID。
public override string UniqueId { get; } // 摘要:
// 使用指定的操作方法参数来执行指定的控制器上下文。
//
// 参数:
// controllerContext:
// 控制器上下文。
//
// parameters:
// 参数。
//
// 返回结果:
// 操作返回值。
//
// 异常:
// System.ArgumentNullException:
// parameters 或 controllerContext 参数为 null。
public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);
//
// 摘要:
// 返回为此成员定义的自定义特性的数组,指定的特性除外。
//
// 参数:
// inherit:
// 要查找继承的自定义特性的层次结构链,则为 true;否则为 false。
//
// 返回结果:
// 自定义特性的数组,如果没有自定义特性,则为空数组。
//
// 异常:
// System.TypeLoadException:
// 无法加载自定义特性类型。
//
// System.Reflection.AmbiguousMatchException:
// 为此成员定义的 attributeType 类型特性不止一个。
public override object[] GetCustomAttributes(bool inherit);
//
// 摘要:
// 返回为此成员定义的自定义特性的数组(按类型标识)。
//
// 参数:
// attributeType:
// 自定义特性的类型。
//
// inherit:
// 要查找继承的自定义特性的层次结构链,则为 true;否则为 false。
//
// 返回结果:
// 自定义特性的数组,如果没有自定义特性,则为空数组。
//
// 异常:
// System.TypeLoadException:
// 无法加载自定义特性类型。
//
// System.Reflection.AmbiguousMatchException:
// 为此成员定义的 attributeType 类型特性不止一个。
public override object[] GetCustomAttributes(Type attributeType, bool inherit);
//
// 摘要:
// 获取筛选器特性。
//
// 参数:
// useCache:
// 若要使用缓存,则为 true,否则为 false。
//
// 返回结果:
// 筛选器特性。
public override IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache);
//
// 摘要:
// 检索操作方法的参数。
//
// 返回结果:
// 操作方法的参数。
public override ParameterDescriptor[] GetParameters();
//
// 摘要:
// 检索操作选择器。
//
// 返回结果:
// 操作选择器。
public override ICollection<ActionSelector> GetSelectors();
//
// 摘要:
// 指示是否为此成员定义某个自定义特性类型的一个或多个实例。
//
// 参数:
// attributeType:
// 自定义特性的类型。
//
// inherit:
// 要查找继承的自定义特性的层次结构链,则为 true;否则为 false。
//
// 返回结果:
// 如果为此成员定义了自定义特性类型,则为 true;否则为 false。
public override bool IsDefined(Type attributeType, bool inherit);
}

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

ASP.NET MVC5学习笔记之Controller执行ControllerDescriptor和ActionDescriptor的更多相关文章

  1. ASP.NET MVC5学习笔记之Controller同步执行架构分析

    在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码.在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成C ...

  2. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  3. ASP.NET MVC5学习笔记01

    由于之前在项目中也使用MVC进行开发,但是具体是那个版本就不是很清楚了,但是我觉得大体的思想是相同的,只是版本高的在版本低的基础上增加了一些更加方便操作的东西.下面是我学习ASP.NET MVC5高级 ...

  4. ASP.NET MVC5 学习笔记-1 控制器、路由、返回类型、选择器、过滤器

    [TOC] 1. Action 1.1 新建项目 新建项目->Web->Asp.net Web应用程序,选择MVC,选择添加测试. 在解决方案上右键,选择"管理NuGet程序包& ...

  5. ASP.NET MVC5学习笔记之Filter提供体系

    前面我们介绍了Filter的基本使用,但各种Filter要在合适的时机运行起来,需要预先准备好,现在看看ASP.NET MVC框架是怎么做的. 一.Filter集合 在ControlerActionI ...

  6. ASP.NET MVC5学习笔记之Filter基本介绍

    Filter是ASP.NET MVC框架提供的基于AOP(面向方面)设计,提供在Action执行前后做一些非业务逻辑通用处理,如用户验证,缓存等.现在来看看Filter相关的一些类型信息. 一.基本类 ...

  7. ASP.NET MVC5学习笔记之Action参数模型绑定基本过程

    当我们在Controller中定义一个Action,通常会定义一个或多个参数,每个参数称为一个模型,ASP.NET MVC框架提供了一种机制称为模型绑定,会尝试自动从请求的信息中实例化每一个模型并赋值 ...

  8. ASP.NET MVC4学习笔记之Controller的激活

    一. 高层相关类说明 当路由系统根据请求Url收集路由信息后,下一步就要将路由信息传给Controller激活系统,Controller激活系统负责实现了IController接口的Controlle ...

  9. ASP.NET MVC5 学习笔记-4 OWIN和Katana

    1. Owin OWIN全名:Open Web Interface for .NET. 它是一个说明,而非一个框架,该声明用来实现Web服务器和框架的松耦合.它提供了模块化.轻量级和便携的设计.类似N ...

随机推荐

  1. nyoj 97 兄弟郊游问题

    点击打开链接 兄弟郊游问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:2 描述 兄弟俩骑车郊游,弟弟先出发,每分钟X米,M分钟后,哥哥带一条狗出发.以每分钟Y米的速度去追弟弟 ...

  2. iOS UILabel详解

    1.创建 CGRect rect = CGRectMake(100, 200, 50, 50); UILabel *label = [[UILabel alloc] initWithFrame:rec ...

  3. RDD中cache和persist的区别

    通过观察RDD.scala源代码即可知道cache和persist的区别: def persist(newLevel: StorageLevel): this.type = { if (storage ...

  4. awk 解析maps文件中的地址

    maps文件一般是这个样子: pi@raspberrypi:~ $ sudo cat /proc//maps 54b88000-54c8d000 r-xp b3: /lib/systemd/syste ...

  5. 关于ORA-04021解决办法(timeout occurred while waiting to lock object)

    某个应用正在锁定该表或者包 表为 select b.SID,b.SERIAL#,c.SQL_TEXT from v$locked_object a, v$session b, v$sqlarea c ...

  6. powerdesigner中怎么给一主键设为自增型auto increme

    在使用powerdesigner 设计数据库表时,通常要对主键进行设置,如果主键是int 类型,一般会设置成自增,那么怎么在 powerdesigner 中设置呢,以下是具体的方法: 在所要设为自增型 ...

  7. DownLoadFile - FileHandler

    C# 跳转新页面 判断URL文件是不是在于在. C# 指定物理目录下载文件,Response.End导致“正在中止线程”异常的问题 public class FileHandler { public ...

  8. oracle中的针对该库的表

    ALL_TAB_COLUMNS:所有用户的表字段 USER_TAB_COMMENTS:当前用户的所有表备注 USER_COL_COMMENTS:当前用户的所有列备注 USER_TAB_COLUMNS: ...

  9. curl返回常见错误码

    转自:http://blog.csdn.net/cwj649956781/article/details/8086337 CURLE_OK() 所有罚款.继续像往常一样. CURLE_UNSUPPOR ...

  10. sql语句小练习二

    1.创建一个数据库StudentManage, 初始化大小10M,不足以1M每次增长 create database StudentManage   on ( name = 'StudentManag ...