前言
在上一篇文章中,我们已经知道了在 ASP.NET Core MVC 中如何发现一个 Action,那么在发现了Action之后,就是 Action 的一个调用过程,也就是整个 Action 执行的生命周期,那么本文我们就来一起看一下Action是怎么激活并且执行的吧。
Getting Started还是从 MvcRouteHandler 的 RouteAsync()开始说起,在上一篇的结尾中,我们已经拿到了 actionDescriptor 这个对象,接着,MVC 会把 actionDescriptor 和 routeData 已经 HttpContext 对象一起包装成为一个 ActionContext 上下文对象。
ActionContext:
public class ActionContext { public ActionDescriptor ActionDescriptor {get; set;} public HttpContext HttpContext {get; set;} public ModelStateDictionary ModelState {get;} public RouteData RouteData {get; set;} }
在有了 ActionContext 之后,接下来就是创建 ControllerActionInvoker 的过程,MVC 会通过一个工厂(ActionInvokerFactory)来创建 Action 对应的 Invoker 对象。
创建 ActionInvoker 的过程如下:
代码如下:
context.Handler = (c) => { var routeData = c.GetRouteData(); var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor); var invoker = _actionInvokerFactory.CreateInvoker(actionContext); return invoker.InvokeAsync(); };
开始调用 invoker.InvokerAsync()InvokerAsync 中的过程其实就是一个管线过滤器执行的过程,在这个管线中,每一步 Action的流转都具有当前管线执行的一个状态,这个过程有点像中间件的执行过程,我们一起来看下。
在 MVC Core 中,过滤器分为 5 大类,分别是:
关于过滤器的管道执行主要包含两个类,分别是 ResourceInvoker 和 ControllerActionInvoker。 其中 ControllerActionInvoker 继承自ResourceInvoker 并且实现了 IActionInvoker 接口。
下面,我们就来看一下 源码 中关于这一部分是如何实现的。
ResourceInvoker
在 ResourceInvoker 中,拥有管线的入口函数,即 InvokeAsync(),在这个函数中又调用了 InvokeFilterPipelineAsync()。
以下这个函数是调用过程的核心函数:
private async Task InvokeFilterPipelineAsync() { var next = State.InvokeBegin; scope = Scope.Invoker; // 'state'用于状态之间的转换期间的内部状态处理。 // 实际上,也就是说将过滤器实例存储在“state”中,然后在下一个状态下检索它。 var state = (object)null; // 当到达终点 state 时, `isCompleted` 会被设置为true var isCompleted = false; while (!isCompleted) { await Next(ref next, ref scope, ref state, ref isCompleted); } }
可以看到,里面有一个Next循环,直到 isCompleted 为 true 时才停止,那么在这个 Next 中会依次执行各个过滤器。
管线针对于MVC预设的几个过滤器他们对应的执行顺序如下图:
在 ResourceInvoker 中,主要处理两种类型的过滤器,他们是 Authorization Filters 和 Resource Filter 。
在 Next 中,会把处理的每个阶段的状态作为一个枚举类型,然后使用的 Switch Case 手动进行的状态的处理,那么在处理 Authorization 相关的过滤器的时候,其 State 具有以下状态:
AuthorizationBegin, AuthorizationNext, AuthorizationAsyncBegin, AuthorizationAsyncEnd, AuthorizationSync, AuthorizationShortCircuit, AuthorizationEnd,关于每一种状态的处理大家可以直接看这里的
,比较简单就不详细说了。
有一点需要说明的是 在 过滤器的处理中,AuthorizationShortCircuit 这个状态是用来处理短路的情况的,也就是说在过滤器中如果对 Context.Result 赋值了,那么会短路过滤器的管道,直接将 isCompleted 标记为 true,结束管道流程。
AuthorizationSync 这个状态是执行 OnAuthorization 的关键流程,IAuthorizationFilter 的实现将在这个流程中执行。
Resource Filter 是 ASP.NET Core MVC 中新增加的一个过滤器,它在管道中的调用顺序仅次于Authorization,在其后执行。用户可以实现 IResourceFilter 或 IAsyncExceptionFilter 接口来自定义 Resource 过滤器, 它主要是此过滤器来实现一些需要短路过滤器管线的一些操作。比如说:缓存(如果被命中,则直接返回,短路管线)。
下面是 Resource 中涉及的所有状态。
ResourceBegin, ResourceNext, ResourceAsyncBegin, ResourceAsyncEnd, ResourceSyncBegin, ResourceSyncEnd, ResourceShortCircuit, ResourceInside, ResourceOutside, ResourceEndControllerActionInvoker
ControllerActionInvoker 重写了 ResourceInvoker 中的InvokeInnerFilterAsync()方法,然后经由 ControllerActionInvokerProvider 实例化。
既然流程是从 ResourceInvoker 开始的,那么我们看一下在 ResourceInvoker 中哪一步调用了 InvokeInnerFilterAsync()。
在 ResourceInvoker 中主要有两个状态来调用它,一个是 State.ResourceNext,另外一个是 State.ResourceInside。
State.ResourceNext 中如果没有检测到有 Resource Filter 这直接开始下一阶段的 Filter调用,即 ControllerActionInvoker 中处理的过滤器。