系列目录

Action的定位

再次回到Controller的ExecuteCore方法,回到action调用的入口:

if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) 

这里的ActionInvoker是个IActionInvoke,它无疑是负责了所有action的调用逻辑,MVC中默认实现这个接口的是ControllerActionInvoke。可以想象ControllerActionInvoke面临的第一个问题是如何找到与actionName对应的action。

首先,并不是所有的action都合法,需要符合下面条件:

1、Public,非static

2、非Controller或其基类定义的方法,比如ToString(),GetHashcode()

3、不能是特殊名字System.Reflection.MethodBase的IsSpecialName标志,例如构造函数,属性包装器,事件包装器

4、具有泛型参数的方法尽管会被认为是action,但框架试图执行这样的action时会只是抛出异常。

其次,MVC为action的查找过程设计了两种“选择器”

  • ActionNameSelectorAttribute
  • ActionMethodSelectorAttribute

如果对此比较迷茫的话,看了他们的派生特性,你也许就明白了:

image

image

 

 

我们常用ActionNameAttribute来“伪装”;用HttpPostAttributeHttpGetAttribute来说明某个action符合的特定的http行为;用NoAction隐藏我们不想“暴露给url”的方法…其实这些行为的原理便是利用MVC为我们提供的action选择的机制。下面这张图说明了这个过程的完成逻辑:

image

ContollerActionInvoker.InvokeAction在内部创建一个ReflectedControllerDescriptor,并调用其FindAction方法,这个对象的FindAction负责调用一个ActionMethodSelector对象实现上述逻辑,并在最后返回一个ReflectedActionDescriptor。ActionMethodSelector对象故名思意,原理上用反射执行上述过程,执行的结果要么返回一个MethodInfo,要么返回null,要么抛出异常。ReflectedControllerDescriptor对返回值判断,如果是null,则也返回null;如果是MethodInfo,则将其包装成ReflectedActionDescriptor返回。对于外面的InvokeAction方法来说只要ReflectedControllerDescriptor返回的ReflectedActionDescriptor为空就返回false,否则继续。Controller负责对返回false的情况调用HandleUnknownAction,该方法默认产生404错误,我们可以重写它。

在回过头来看看上图的逻辑,不难得到这样一个事实:ActionNameSelectorAttribute优先于ActionMethodSelectorAttribute,ActionMethodSelectorAttribute优先于没有ActionMethodSelectorAttribute特性的方法

更多关于这部分源码的细节,可以参考:通过源代码研究ASP.NET MVC中的Controller和View(六)

 

MVC的内建过滤器

ActionDescriptor(ReflectedActionDescriptor)返回后,ContollerActionInvoker开始执行action,但是在执行action前还有一些额外的步骤,那就是过滤。以下四个是MVC内建的过滤接口:

  • IActionFilter
  • IAuthorizationFilter
  • IExceptionFilter
  • IResultFilter

这四个Filter会被ControllerActionInvoker框架综合考量,下面的伪代码说明了ControllerActionInvoker处理他们的逻辑顺序:

try
{
	//依次执行IAuthorizationFilter
	Run each IAuthorizationFilter's OnAuthorization() method
	//所有的IAuthorizationFilter都返回null而不是ActionResult
	if(none of the IAuthorizationFilters cancelled execution)
	{
		Run each IActionFilter's OnActionExecuting() method
		Run the action method
		//注意到这里按照反转顺序进行
		Run each IActionFilter's OnActionExecuted() method (in reverse order)
		Run each IResultFilter's OnResultExecuting() method
		Run the action result
		//注意到这里按照反转顺序进行
		Run each IResultFilter's OnResultExecuted() method (in reverse order)
	}
	//有IAuthorizationFilter返回ActionResult
	else
	{
		Run any action result set by the authorization filters
	}
}
catch(exception not handled by any action or result filter)
{
	//注意到IExceptionFilter处理的异常只在这个try catch块中
	Run each IExceptionFilter's OnException() method
	Run any action result set by the exception filters
}

需要注意的事项,在代码中都已经给出注释,在后面一节中,将讨论其中的某些细节。

MVC中与这四个接口相关的特性如下:

image

我们熟悉的有AuthorizeOutputCacheChildAction等。曾经,对于这些形形色色的Attribute,我都不能很好的理解和归类,现在总算是找它们各自的归宿了。

下一节,将针对这四个过滤器接口深入讨论一些细节问题。

 

劳动果实,转载请注明出处:http://www.cnblogs.com/P_Chou/archive/2010/12/01/details-asp-net-mvc-07.html