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

时间:2021-11-25 02:48:43

  在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码。
在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成Controller的实例。这一节来了解一下Controller的总体执行分析,以同步执行版本为主。

Controller的继承体系如下图所示:

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

当调用Controller实例的Excecute方法时,实际是调用ControllerBase的Excecute方法,该方法的主要实现代码如下:

 protected virtual void Execute(RequestContext requestContext)
{ VerifyExecuteCalledOnce();
Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
ExecuteCore();
}
}

该方法主要为Action的执行做一些初始化工作,Initialize(requestContext)实例化ControllerContext, ScopeStorage.CreateTransientScope方法初始化临时存储环境,这个储存主要用于页面HtmlHlper类中,具体的后面遇到再分析。最后了调用了抽象方法ExecuteCore,在Controller中实现该方法,我们看看该方法的代码:

 protected override void ExecuteCore()
{
PossiblyLoadTempData();
try
{
string actionName = GetActionName(RouteData);
if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
{
HandleUnknownAction(actionName);
}
}
finally
{
PossiblySaveTempData();
}
}

PossiblyLoadTempData, PossiblySaveTempData这一对方法主要是帮助处理Controller的TempData属性中临时数据的加载和保存,默认是基于SessionStateTempDataProvider,也就是TempData的值默认保存在Session中,我们可以在会话范围内使用TempData。接下来的代码很简单直接就是调用ActionInvoker的InvokeAction方法并传递上下文参数和action名称。ActionInvoker是一个类型为IActionInvoker的属性, 它内部实现调用CreateActionInvoker方法,具体的实现如下:

return Resolver.GetService<IAsyncActionInvoker>() ?? Resolver.GetService<IActionInvoker>() ?? new AsyncControllerActionInvoker();

Resolver默认情况下是没有注册IActionInvoker的,可见默认情况下IActionInvoker的实例是AsyncControllerActionInvoker。我们现在来具体看一下IActionInvoker的继承体系.如下图所示:

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

在这里我们仅分析同步版本的ControllerActionInvoker的实现。现在来看看其InvokeAction的方法, 这个方法实现比较复杂,主要的代码如下:

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{ //省略检查代码
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName); if (actionDescriptor != null)
{
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor); try
{
AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor); if (authenticationContext.Result != null)
{
// An authentication filter signaled that we should short-circuit the request. Let all
// authentication filters contribute to an action result (to combine authentication
// challenges). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authenticationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authenticationContext.Result);
}
else
{
AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authorizationContext.Result != null)
{
// An authorization filter signaled that we should short-circuit the request. Let all
// authentication filters contribute to an action result (to combine authentication
// challenges). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authorizationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
} IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters); // The action succeeded. Let all authentication filters contribute to an action result (to
// combine authentication challenges; some authentication filters need to do negotiation
// even on a successful result). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
postActionContext.Result);
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters,
challengeContext.Result ?? postActionContext.Result);
}
}
}
catch (ThreadAbortException)
{
// This type of exception occurs as a result of Response.Redirect(), but we special-case so that
// the filters don't see this as an error.
throw;
}
catch (Exception ex)
{
// something blew up, so execute the exception filters
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
if (!exceptionContext.ExceptionHandled)
{
throw;
}
InvokeActionResult(controllerContext, exceptionContext.Result);
} return true;
} // notify controller that no method matched
return false;
}

方法内大致做了以下几件事情:

  1. 调用GetControllerDescriptor方法获取ControllerDescriptor类的实例
  2. 调用FinAction方法获取ActionDescriptor类的实例
  3. 收集应用在Action上所有Filter信息
  4. 执行Filter和对应的Action方法
  5. 执行结果ActionResult并返回, true表示找到Acion, false表示没有匹配的Action

上面的每一条都包含很多处理,这里稍微在较高层解说一下相关的类型,具体的后面每个小节解释一个话题 。

1. ControllerDescriptor和ActionDescriptor分别表示Controller和Action元数据的相关描述类型

2. Filter 是提供了ASP.NET MVC 提供的一种 面向方面(AOP)的编程方式,用于Controler或Action执行前后做一些通用处理,如安全验证,授权,异常处理等.

大概有以下几种Filter类型:

IAuthentiactionFilter 表示自定义验证,这个是ASP.NET MVC5新增的类型, OnAuthentication表示执行验证检查,你也许要更改AuthenticationContext的Principal属性,ASP.NET MVC会检查该属性,发现有更改将使用你设置的Principal, OnAuthenticationChallenge方法有点特别,不管是Action或其它的Filter执行失败或成功都会调用OnAuthenticationChallenge方法(异常除外),OnAuthenticationChallenge方法为你的自定义验证写回Http响应提供机会, 如你也许要改写Http header

public interface IAuthenticationFilter
{
/// <summary>Authenticates the request.</summary>
/// <param name="filterContext">The context to use for authentication.</param>
void OnAuthentication(AuthenticationContext filterContext); /// <summary>Adds an authentication challenge to the current <see cref="ActionResult"/>.</summary>
/// <param name="filterContext">The context to use for the authentication challenge.</param>
void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext);
}

IAuthenticationFilter表示授权检查

 public interface IAuthorizationFilter
{
void OnAuthorization(AuthorizationContext filterContext);
}

IActionFilter表示Action执行前后做一些处理

 public interface IActionFilter
{
void OnActionExecuting(ActionExecutingContext filterContext);
void OnActionExecuted(ActionExecutedContext filterContext);
}

IResultFilter表示ActionResult执行前后做一些处理

public interface IResultFilter
{
void OnResultExecuting(ResultExecutingContext filterContext);
void OnResultExecuted(ResultExecutedContext filterContext);
}

IExceptionFilter表示异常通用处理

 public interface IExceptionFilter
{
void OnException(ExceptionContext filterContext);
}

Filter的总体执行流程如下图所示:

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

下一节分析一下ControllerDescriptor和ActionDescriptor