ASP.NET Core MVC 源码学习:详解 Action 的激活
前言
在上一篇文章中,我们已经知道了在 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` 会被设置为truevar 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 返回结果,执行后返回 EmptyResultif (returnType == typeof(void)){executor.Execute(controller, orderedArguments);result = new EmptyResult();}// Task 返回结果,执行后返回 EmptyResultelse if (returnType == typeof(Task)){await (Task)executor.Execute(controller, orderedArguments);result = new EmptyResult();}// IActionResult 返回结果,执行后返回 IActionResultelse 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){//是否为异步Actionif (_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
欢迎转载,请在明显位置给出出处及链接
转载于:https://www.cnblogs.com/savorboard/p/aspnetcore-mvc-action.html
ASP.NET Core MVC 源码学习:详解 Action 的激活相关推荐
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...
- ASP.NET Core MVC 源码学习:详解 Action 的匹配
前言 在 上一篇 文章中,我们已经学习了 ASP.NET Core MVC 的启动流程,那么 MVC 在启动了之后,当请求到达过来的时候,它是怎么样处理的呢? 又是怎么样把我们的请求准确的传达到我们的 ...
- ASP.NET Core MVC 源码学习:Routing 路由
前言 最近打算抽时间看一下 ASP.NET Core MVC 的源码,特此把自己学习到的内容记录下来,也算是做个笔记吧. 路由作为 MVC 的基本部分,所以在学习 MVC 的其他源码之前还是先学习一下 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)(转)
阅读目录 一.MVC原理解析 1.MVC原理 二.HttpHandler 1.HttpHandler.IHttpHandler.MvcHandler的说明 2.IHttpHandler解析 3.Mvc ...
- MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)
前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...
- ASP.NET Core 框架源码地址
ASP.NET Core 框架源码地址 https://github.com/dotnet/corefx 这个是.net core的 开源项目地址 https://github.com/aspnet ...
- jsp漂亮的登录界面源码_【案例+源码】详解MVC框架模式及其应用
案例+源码]详解MVC框架模式及其应用 写在开头: 首先我们需要知道,框架模式.模式.开发模式是三种不同的概念,但他们的目的都一样:解耦! 1.关于MVC框架模型 MVC是三个单词的缩写: M,Mod ...
- Android 源码编译详解【合集篇】
Android 源码编译详解[一]:服务器硬件配置及机型推荐 做 Android系统开发多年,开发环境都是入职就搭建好了,入职时拿个账号密码就直接开始搞开发了,年初换了新公司,所有的项目都是刚起步,一 ...
- ASP.NET Core 中间件(Middleware)详解
ASP.NET Core 中间件(Middleware)详解 原文:ASP.NET Core 中间件(Middleware)详解 本文为官方文档译文,官方文档现已非机器翻译 https://docs. ...
- Android四大组件之bindService源码实现详解
Android四大组件之bindService源码实现详解 Android四大组件源码实现详解系列博客目录: Android应用进程创建流程大揭秘 Android四大组件之bindServic ...
最新文章
- ironpython 教程_「ironpython」VS2017 IronPython做界面
- Hibernate中的数据库方言(Dialect)
- 如何做好数字化体验管理,了解一下?
- javaweb学习总结(二十九)——EL表达式
- Win8节省C盘空间攻略
- SAP Spartacus维护CMS Component到Angular Component的源代码位置
- delete postman 传参_PostMan 传参boolean 类型,接口接受的值一直是false
- 2058. 找出临界点之间的最小和最大距离
- 文件拷贝(字符、字节)
- Java 方法使用总结(重载、数组输出、enum和switch、foreach和迭代器、可变长度参数、重载中使用可变长度参数)
- 云主机挂载硬盘 - 开机自动挂载
- springcloud2.0以上版本_eureka控制台显示_找不到${spring.cloud.client.ipAddress}_没有显示成IP地址---springcloud工作笔记165
- 热烈庆贺:一个月,由70名升级为60名!
- Java数据类型转换
- numberformat_解决NumberFormat的解析问题
- Win10系统隐藏磁盘
- 计算机网络课程设计家庭网,家庭无线局域网的组建
- 灰灰深入浅出讲解支持向量机(SVM)
- TDK是什么意思,TDK怎么写?怎么利于SEO优化?
- 下载了免费的epub电子书,如何用Windows电脑打开?
热门文章
- Router OS 全攻略
- include问题及错误解决
- mysql数据库调试_mysql数据库调试
- appscan无法连接到服务器_和平精英无法连接到服务器是怎么回事 最新解决方案...
- [编程题]手机屏幕解锁模式
- 1010 一元多项式求导 (25 分)—PAT (Basic Level) Practice (中文)
- L1-009 N个数求和 (20 分)—团体程序设计天梯赛
- python 粘包问题
- 51nod 1106 质数检测
- 【OOM】GC overhead limit exceeded