文章出處

前言

上一篇文章中,我們已經知道了在 ASP.NET Core MVC 中如何發現一個 Action,那么在發現了Action之后,就是 Action 的一個調用過程,也就是整個 Action 執行的生命周期,那么本文我們就來一起看一下Action是怎么激活并且執行的吧。

Getting Started

還是從 MvcRouteHandlerRouteAsync()開始說起,在上一篇的結尾中,我們已經拿到了 actionDescriptor 這個對象,接著,MVC 會把 actionDescriptorrouteData 已經 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)

關于過濾器的管道執行主要包含兩個類,分別是 ResourceInvokerControllerActionInvoker。 其中 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 FiltersResource 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,在其后執行。用戶可以實現 IResourceFilterIAsyncExceptionFilter 接口來自定義 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

實現了 IResultFilterIAsyncResultFilter 接口。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 FilterOnActionExecuting 后執行。

用戶代碼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
歡迎轉載,請在明顯位置給出出處及鏈接


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()