ASP.NET Core MVC 源码学习:详解 Action 的激活

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011966339/article/details/90199266

前言

我们已经知道了在 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 的过程中,过滤器管线中都做了哪些工作,并且讲解了其中的过程,以及各个过滤器的一些作用和功能。

猜你喜欢

转载自blog.csdn.net/u011966339/article/details/90199266
今日推荐