在上一篇文章中,我们已经知道了在 ASP.NET Core MVC 中如何发现一个 Action,那么在发现了 Action 之后,就是 Action 的一个调用过程,也就是整个 Action 执行的生命周期,那么本文我们就来一起看一下 Action 是怎么激活并且执行的吧。
还是从
的
- MvcRouteHandler
开始说起,在上一篇的结尾中,我们已经拿到了
- RouteAsync()
这个对象,接着,MVC 会把
- actionDescriptor
和
- actionDescriptor
已经 HttpContext 对象一起包装成为一个
- routeData
上下文对象。
- 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
的过程,MVC 会通过一个工厂(
- ControllerActionInvoker
)来创建 Action 对应的 Invoker 对象。
- ActionInvokerFactory
创建 ActionInvoker 的过程如下:
代码如下:
- context.Handler= (c) =>
- {varrouteData = c.GetRouteData();varactionContext =new ActionContext(context.HttpContext, routeData, actionDescriptor);varinvoker = _actionInvokerFactory.CreateInvoker(actionContext);returninvoker.InvokeAsync();
- };
中的过程其实就是一个管线过滤器执行的过程,在这个管线中,每一步 Action 的流转都具有当前管线执行的一个状态,这个过程有点像中间件的执行过程,我们一起来看下。
- InvokerAsync
在 MVC Core 中,过滤器分为 5 大类,分别是:
关于过滤器的管道执行主要包含两个类,分别是
和
- ResourceInvoker
。 其中
- ControllerActionInvoker
继承自
- ControllerActionInvoker
并且实现了
- ResourceInvoker
接口。
- IActionInvoker
下面,我们就来看一下 源码 中关于这一部分是如何实现的。
ResourceInvoker
在
中,拥有管线的入口函数,即
- ResourceInvoker
,在这个函数中又调用了
- InvokeAsync()
。
- InvokeFilterPipelineAsync()
以下这个函数是调用过程的核心函数:
- privateasync TaskInvokeFilterPipelineAsync()
- {varnext = State.InvokeBegin;// scope 用来标明下一个调用的方法是哪个,
- // 以及初始化的状态是哪种。
- // 最外面的"scope"是"Scope.Invoker",不需要任何类型的"context"或"result"。
- varscope = Scope.Invoker;// 'state'用于状态之间的转换期间的内部状态处理。
- // 实际上,也就是说将过滤器实例存储在"state"中,然后在下一个状态下检索它。var state = (object)null;// 当到达终点 state 时, `isCompleted` 会被设置为true
- varisCompleted =false;while(!isCompleted)
- {
- awaitNext(refnext,refscope,refstate,refisCompleted);
- }
- }
可以看到,里面有一个 Next 循环,直到 isCompleted 为 true 时才停止,那么在这个
中会依次执行各个过滤器。
- Next
管线针对于 MVC 预设的几个过滤器他们对应的执行顺序如下图:
在
中,主要处理两种类型的过滤器,他们是 Authorization Filters 和 Resource Filter 。
- ResourceInvoker
在 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
接口来自定义 Resource 过滤器, 它主要是此过滤器来实现一些需要短路过滤器管线的一些操作。比如说:缓存(如果被命中,则直接返回,短路管线)。
- IAsyncExceptionFilter
下面是 Resource 中涉及的所有状态。
- ResourceBegin,
- ResourceNext,
- ResourceAsyncBegin,
- ResourceAsyncEnd,
- ResourceSyncBegin,
- ResourceSyncEnd,
- ResourceShortCircuit,
- ResourceInside,
- ResourceOutside,
- ResourceEnd
ControllerActionInvoker
重写了
- ControllerActionInvoker
中的
- ResourceInvoker
方法,然后经由
- InvokeInnerFilterAsync()
实例化。
- ControllerActionInvokerProvider
既然流程是从
开始的,那么我们看一下在
- ResourceInvoker
中哪一步调用了
- ResourceInvoker
。
- InvokeInnerFilterAsync()
在
中主要有两个状态来调用它,一个是
- ResourceInvoker
,另外一个是
- State.ResourceNext
。
- State.ResourceInside
中如果没有检测到有 Resource Filter 这直接开始下一阶段的 Filter 调用,即
- State.ResourceNext
中处理的过滤器。
- ControllerActionInvoker
中会将下一阶段的状态设置为
- State.ResourceInside
,然后开始启动调用
- State.ResourceOutside
,当
- InvokeInnerFilterAsync()
中的过滤器执行完成之后,再回过头来执行
- ControllerActionInvoker
相关内容。
- ResourceOutside
好了,现在流程已经正式来到了
。
- ControllerActionInvoker
在
中,主要处理 3 种类型的过滤器。
- ControllerActionInvoker
实现了
接口或
- IExceptionFilter
接口的过滤器,处理包括发生在 Controller 创建及 模型绑定 期间出现的异常。它们只在管道内发生异常时才会被调用。
- IAsyncExceptionFilter
实现了
接口或
- IActionFilter
接口的过滤器,它们可以在 action 方法执行的前后被执行。
- IAsyncActionFilter
实现了
或
- IResultFilter
接口。Result Filter 在 Action Result 执行体的周围执行。当 Action 或 Action 过滤器产生 Action 结果时,只有成功运行的才会执行结果过滤器。如果异常过滤器处理了异常,那么结果过滤器就不会运行——除非异常过滤器将异常设置为 null。
- IAsyncResultFilter
源码流程
下面是
中的
- ControllerActionInvoker
- InvokeInnerFilterAsync
- protected overrideasync TaskInvokeInnerFilterAsync()
- {varnext = State.ResourceInsideBegin;varscope = Scope.Resource;
- var state = (object)null;varisCompleted =false;while(!isCompleted)
- {
- awaitNext(refnext,refscope,refstate,refisCompleted);
- }
- }
它的起始状态从
开始,核心方法还是 Next 方法。
- State.ResourceInsideBegin
在源代码中,状态的流转是先从 Exception 开始,然后对 Exception 过滤器进行 "压栈",但是并不会执行过滤器中的代码,接着会执行 Action 相关状态代码,在
这个状态中会执行 Action Filters 中的
- State.ActionAsyncBegin
,然后在
- OnActionExecuting
这个状态中执行
- State.ActionSyncEnd
。
- OnActionExecuted
注意: Action Filter 的执行代码由一个
代码块包装,当发生异常的时候,MVC 会把这些信息包装成为一个
- Try Catch
对象,然后会接着执行 Action Filter 里面
- ActionExecutedContext
。
- OnActionExecuted
等 Action Filter 相关的过滤器执行完成之后会将状态置为
开始执行 Exception Filter 相关业务。
- State.ExceptionSyncEnd
在 整个管道的流程中,用户在 Action 中的代码是在
这个状态中执行的,它在 Action Filter 的
- State.ActionInside
后执行。
- OnActionExecuting
用户代码 Action 的调用主要是下面这个函数:
- privateasync TaskInvokeActionMethodAsync()
- {varcontrollerContext = _controllerContext;varexecutor = _executor;varcontroller = _controller;vararguments = _arguments;//构建Action参数
- varorderedArguments = ControllerActionExecutor.PrepareArguments(arguments, executor);
- IActionResult result =null;varreturnType = executor.MethodReturnType;// void 返回结果,执行后返回 EmptyResult
- if(returnType ==typeof(void))
- {
- executor.Execute(controller, orderedArguments);
- result =new EmptyResult();
- }// Task 返回结果,执行后返回 EmptyResult
- else if(returnType ==typeof(Task))
- {await(Task)executor.Execute(controller, orderedArguments);
- result =new EmptyResult();
- }// IActionResult 返回结果,执行后返回 IActionResult
- else if(executor.TaskGenericType==typeof(IActionResult))
- {
- result =await(Task)executor.Execute(controller, orderedArguments);if(result ==null)
- {throw new InvalidOperationException(
- Resources.FormatActionResult_ActionReturnValueCannotBeNull(typeof(IActionResult)));
- }
- }//是否为 IActionResult 的子类型
- else if(executor.IsTypeAssignableFromIActionResult)
- {//是否为异步Action
- if(_executor.IsMethodAsync)
- {
- result = (IActionResult)await _executor.ExecuteAsync(controller, orderedArguments);
- }else{
- result = (IActionResult)_executor.Execute(controller, orderedArguments);
- }if(result ==null)
- {throw new InvalidOperationException(
- Resources.FormatActionResult_ActionReturnValueCannotBeNull(_executor.TaskGenericTypereturnType));
- }
- }//非异步方法
- else if(!executor.IsMethodAsync)
- {varresultAsObject = executor.Execute(controller, orderedArguments);
- result = resultAsObjectasIActionResultnew ObjectResult(resultAsObject)
- {
- DeclaredType = returnType,
- };
- }else if(executor.TaskGenericType!=null)
- {varresultAsObject = await executor.ExecuteAsync(controller, orderedArguments);
- result = resultAsObjectasIActionResultnew ObjectResult(resultAsObject)
- {
- DeclaredType = executor.TaskGenericType,
- };
- }else{throw new InvalidOperationException(Resources.FormatActionExecutor_UnexpectedTaskInstance(
- executor.MethodInfo.Name,
- executor.MethodInfo.DeclaringType));
- }
- _result = result;
- }
下面是
中用到的所有状态。
- ControllerActionInvoker
- private enum State
- {
- ResourceInsideBegin,
- ExceptionBegin,
- ExceptionNext,
- ExceptionAsyncBegin,
- ExceptionAsyncResume,
- ExceptionAsyncEnd,
- ExceptionSyncBegin,
- ExceptionSyncEnd,
- ExceptionInside,
- ExceptionHandled,
- ExceptionEnd,
- ActionBegin,
- ActionNext,
- ActionAsyncBegin,
- ActionAsyncEnd,
- ActionSyncBegin,
- ActionSyncEnd,
- ActionInside,
- ActionEnd,
- ResultBegin,
- ResultNext,
- ResultAsyncBegin,
- ResultAsyncEnd,
- ResultSyncBegin,
- ResultSyncEnd,
- ResultInside,
- ResultEnd,
- ResourceInsideEnd,
- }
本文详细描述了 MVC 在 Action 是如何激活的,以及在激活 Action 的过程中,过滤器管线中都做了哪些工作,并且讲解了其中的过程,以及各个过滤器的一些作用和功能。
如果你对 .NET Core 感兴趣可以关注我,我会定期在博客分享关于 .NET Core 的学习心得,如果你认为本篇文章对你有帮助的话,谢谢你的【推荐】。
来源: http://www.cnblogs.com/savorboard/p/aspnetcore-mvc-action.html