前言
在上一篇文章中,我們已經知道了在 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
歡迎轉載,請在明顯位置給出出處及鏈接
文章列表