ASP.NET Core MVC 源码学习:详解 Action 的激活
2017-04-14 10:59
561 查看
前言
在上一篇文章中,我们已经知道了在 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 大类,分别是:
授权过滤器(Authorization Filters)
资源过滤器(Resource Filters)
Action过滤器(Action Filters)
异常过滤器(Exception Filters)
Result 过滤器(Result Filters)
关于过滤器的管道执行主要包含两个类,分别是
ResourceInvoker和
ControllerActionInvoker。 其中
ControllerActionInvoker继承自
ResourceInvoker并且实现了
IActionInvoker接口。
下面,我们就来看一下 源码 中关于这一部分是如何实现的。
ResourceInvoker
在
ResourceInvoker中,拥有管线的入口函数,即
InvokeAsync(),在这个函数中又调用了
InvokeFilterPipelineAsync()。
以下这个函数是调用过程的核心函数:
private async Task InvokeFilterPipelineAsync() { var next = State.InvokeBegin; // scope 用来标明下一个调用的方法是哪个, // 以及初始化的状态是哪种。 // 最外面的“scope”是“Scope.Invoker”,不需要任何类型的“context”或“result”。 var 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 。
Authorization Filters
在 Next 中,会把处理的每个阶段的状态作为一个枚举类型,然后使用的 Switch Case 手动进行的状态的处理,那么在处理 Authorization 相关的过滤器的时候,其 State 具有以下状态:
AuthorizationBegin, AuthorizationNext, AuthorizationAsyncBegin, AuthorizationAsyncEnd, AuthorizationSync, AuthorizationShortCircuit, AuthorizationEnd,
关于每一种状态的处理大家可以直接看这里的
代码 ,比较简单就不详细说了。
有一点需要说明的是 在 过滤器的处理中,AuthorizationShortCircuit 这个状态是用来处理短路的情况的,也就是说在过滤器中如果对 Context.Result 赋值了,那么会短路过滤器的管道,直接将 isCompleted 标记为 true,结束管道流程。
AuthorizationSync 这个状态是执行 OnAuthorization 的关键流程,IAuthorizationFilter 的实现将在这个流程中执行。
Resource Filters
Resource Filter 是 ASP.NET Core MVC 中新增加的一个过滤器,它在管道中的调用顺序仅次于Authorization,在其后执行。用户可以实现
IResourceFilter或
IAsyncExceptionFilter接口来自定义 Resource 过滤器, 它主要是此过滤器来实现一些需要短路过滤器管线的一些操作。比如说:缓存(如果被命中,则直接返回,短路管线)。
下面是 Resource 中涉及的所有状态。
ResourceBegin, ResourceNext, ResourceAsyncBegin, ResourceAsyncEnd, ResourceSyncBegin, ResourceSyncEnd, ResourceShortCircuit, ResourceInside, ResourceOutside, ResourceEnd
ControllerActionInvoker
ControllerActionInvoker重写了
ResourceInvoker中的
InvokeInnerFilterAsync()方法,然后经由
ControllerActionInvokerProvider实例化。
既然流程是从
ResourceInvoker开始的,那么我们看一下在
ResourceInvoker中哪一步调用了
InvokeInnerFilterAsync()。
在
ResourceInvoker中主要有两个状态来调用它,一个是
State.ResourceNext,另外一个是
State.ResourceInside。
State.ResourceNext中如果没有检测到有 Resource Filter 这直接开始下一阶段的 Filter调用,即
ControllerActionInvoker中处理的过滤器。
State.ResourceInside中会将下一阶段的状态设置为
State.ResourceOutside,然后开始启动调用
InvokeInnerFilterAsync(),当
ControllerActionInvoker中的过滤器执行完成之后,再回过头来执行
ResourceOutside相关内容。
好了,现在流程已经正式来到了
ControllerActionInvoker。
在
ControllerActionInvoker中,主要处理 3 种类型的过滤器。
Exception Filters
实现了
IExceptionFilter接口或
IAsyncExceptionFilter接口的过滤器,处理包括发生在 Controller 创建及 模型绑定 期间出现的异常。它们只在管道内发生异常时才会被调用。
Action Filters
实现了
IActionFilter接口或
IAsyncActionFilter接口的过滤器,它们可以在 action 方法执行的前后被执行。
Result Filters
实现了
IResultFilter或
IAsyncResultFilter接口。Result Filter 在 Action Result 执行体的周围执行。当 Action 或 Action 过滤器产生 Action 结果时,只有成功运行的才会执行结果过滤器。如果异常过滤器处理了异常,那么结果过滤器就不会运行——除非异常过滤器将异常设置为null。
源码流程
下面是
ControllerActionInvoker中的
InvokeInnerFilterAsync
protected override async Task InvokeInnerFilterAsync() { var next = State.ResourceInsideBegin; var scope = Scope.Resource; var state = (object)null; var isCompleted = false; while (!isCompleted) { await Next(ref next, ref scope, ref state, ref isCompleted); } }
它的起始状态从
State.ResourceInsideBegin开始,核心方法还是 Next 方法。
在源代码中,状态的流转是先从 Exception 开始,然后对Exception过滤器进行"压栈",但是并不会执行过滤器中的代码,接着会执行Action相关状态代码,在
State.ActionAsyncBegin这个状态中会执行 Action Filters 中的
OnActionExecuting,然后在
State.ActionSyncEnd这个状态中执行
OnActionExecuted。
注意: Action Filter 的执行代码由一个
Try Catch代码块包装,当发生异常的时候,MVC会把这些信息包装成为一个
ActionExecutedContext对象,然后会接着执行 Action Filter 里面
OnActionExecuted。
等 Action Filter 相关的过滤器执行完成之后会将状态置为
State.ExceptionSyncEnd开始执行 Exception Filter 相关业务。
在 整个管道的流程中,用户在 Action 中的代码是在
State.ActionInside这个状态中执行的,它在 Action Filter 的
OnActionExecuting后执行。
用户代码Action的调用主要是下面这个函数:
private async Task InvokeActionMethodAsync() { var controllerContext = _controllerContext; var executor = _executor; var controller = _controller; var arguments = _arguments; //构建Action参数 var orderedArguments = ControllerActionExecutor.PrepareArguments(arguments, executor); IActionResult result = null; var returnType = 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<IActionResult>)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.TaskGenericType ?? returnType)); } } //非异步方法 else if (!executor.IsMethodAsync) { var resultAsObject = executor.Execute(controller, orderedArguments); result = resultAsObject as IActionResult ?? new ObjectResult(resultAsObject) { DeclaredType = returnType, }; } else if (executor.TaskGenericType != null) { var resultAsObject = await executor.ExecuteAsync(controller, orderedArguments); result = resultAsObject as IActionResult ?? new 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
作者博客:Savorboard
欢迎转载,请在明显位置给出出处及链接
相关文章推荐
- ASP.NET Core MVC 源码学习:详解 Action 的匹配
- ASP.NET Core MVC 源码学习:详解 Action 的匹配
- ASP.NET Core MVC 源码学习:详解 Action 的匹配
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
- 详解ASP.NET Core MVC 源码学习:Routing 路由
- ASP.NET Core MVC源码学习:MVC启动流程详解
- ASP.NET Core MVC 源码学习:Routing 路由
- ASP.NET Core MVC 源码学习:Routing 路由
- ASP.NET Core MVC 源码学习:Routing 路由
- ASP.NET Core MVC 源码学习:Routing 路由
- ASP.NET Core MVC 源码学习:Routing 路由
- ASP.NET MVC Controller激活系统详解:IoC的应用[下篇]
- ASP.NET MVC3 学习笔记----HTML.Action()和HTML.RenderAction()
- 从零开始学习 ASP.NET MVC 1.0 (三) Controller/Action 深入解析与应用实例
- DotText源码学习——ASP.NET的工作机制
- ASP.NET MVC Controller激活系统详解:默认实现
- ASP.NET MVC Controller激活系统详解:默认实现
- 转载 ASP.NET MVC学习之(5):Html.ActionLink