ASP.NET Core 2.2 : 十七.Action的執行(Endpoint.RequestDelegate后面的故事)


          上一章介紹了經過路由的處理,一個請求找到了具體處理這個請求的EndPoint,並最終執行它的RequestDelegate方法來處理這個Httpcontext。本章繼續這個處理進程,按照慣例,依然通過幾幅圖來聊一聊這個RequestDelegate之后的故事。在此就避免不了的聊到各種Filter,它方便我們在action執行的前后做一些 “小動作”。(ASP.NET Core 系列目錄

一、概述

          首先看一下RequestDelegate這個方法:

RequestDelegate requestDelegate = (context) =>
{
    var routeData = context.GetRouteData();
    var actionContext = new ActionContext(context, routeData, action);
    var invoker = _invokerFactory.CreateInvoker(actionContext);

    return invoker.InvokeAsync();
};

          將這個方法的內容分為兩部分:

          A. invoker的生成,前三句,通過CreateInvoker方法生成了一個invoker,它是一個比較復雜的綜合體,包含了controller的創建工廠、參數的綁定方法以及本action相關的各種Filter的集合等, 也就是說它是前期准備工作階段,這個階段擴展開來涉及面比較廣,在下文詳細描述。  

          B.invoker的執行,最后一句,前面已經萬事俱備,准備了好多方法,到此來執行。此時涉及到前面准備的各種方法的執行,各種Filter也在此時被執行。

二、invoker的生成

           依照習慣,還是通過流程圖來看一看:

                                                                                                     圖一(點擊查看大圖

          首先說一下此圖的結構,每個泳道相當於是上一個泳道中的圖標的細化說明,例如第二條泳道是圖標標識的方塊的明細化。

          A. 泳道一:

                  就是第一節【概述】中描述的的內容,不再贅述。另外提一下本文的核心invoker本質上就是一個ControllerActionInvoker,也是圖中的ActionInvokerProviderContext.Result。

          B.泳道二:即①的詳細描述

          ① ActionInvokerFactory.CreateInvoker(actionContext)

 1 public IActionInvoker CreateInvoker(ActionContext actionContext)
 2 {
 3     var context = new ActionInvokerProviderContext(actionContext);
 4 
 5     foreach (var provider in _actionInvokerProviders)
 6     {
 7         provider.OnProvidersExecuting(context);
 8     }
 9 
10     for (var i = _actionInvokerProviders.Length - 1; i >= 0; i--)
11     {
12         _actionInvokerProviders[i].OnProvidersExecuted(context);
13     }
14 
15     return context.Result;
16 }

          本章設計的這部分內容比較常見的一個操作就是context的封裝,這從第一個泳道的第一個操作就開始了, 他將HttpContext、RouteData,ActionDescriptor封裝到一起成了一個ActionContext ,而到了這個方法,又將這個ActionContext 封裝成了ActionInvokerProviderContext,接下來就是遍歷_actionInvokerProviders調用它們的OnProvidersExecuting和OnProvidersExecuted方法來設置ActionInvokerProviderContext.Result,也就是最終的ControllerActionInvoker。

          這里說一下_actionInvokerProviders,它的類型是IActionInvokerProvider[],默認情況下包含了兩個,分別是ControllerActionInvokerProvider和PageActionInvokerProvider,第一個是用於MVC的action的處理,而第二個用於Razor Pages Web的處理。二者的OnProvidersExecuting方法都會首先判斷當前action是不是自己對應的類型,若不是則直接跳過。二者的OnProvidersExecuted方法目前均為空。所以圖中和下面關於OnProvidersExecuting的描述也僅限於ControllerActionInvokerProvider的OnProvidersExecuting方法。

          C.泳道三:ControllerActionInvokerProvider.OnProvidersExecuting(context)

          即泳道二中的③的詳細描述

 1 public void OnProvidersExecuting(ActionInvokerProviderContext context)
 2 {
 3     if (context.ActionContext.ActionDescriptor is ControllerActionDescriptor)
 4     {
 5         var controllerContext = new ControllerContext(context.ActionContext);
 6         // PERF: These are rarely going to be changed, so let's go copy-on-write.
 7         controllerContext.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories);
 8         controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors;
 9 
10         var cacheResult = _controllerActionInvokerCache.GetCachedResult(controllerContext);
11 
12         var invoker = new ControllerActionInvoker(
13             _logger,
14             _diagnosticSource,
15             _mapper,
16             controllerContext,
17             cacheResult.cacheEntry,
18             cacheResult.filters);
19 
20         context.Result = invoker;
21     }
22 }

         如上文所述,在處理之前,首先就是判斷當前action是否是自己對應處理的類型。然后就是繼續封裝大法,將ActionContext封裝成了ControllerContext。進而是調用GetCachedResult方法讀取兩個關鍵內容cacheResult.cacheEntry和cacheResult.filters后,將其封裝成ControllerActionInvoker(⑤)。

          D.第四條泳道:

          對應的是第三條中的④ControllerActionInvokerCache.GetCachedResult(controllerContext);

 1 public (ControllerActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters) GetCachedResult(ControllerContext controllerContext)
 2 {
 3     var cache = CurrentCache;
 4     var actionDescriptor = controllerContext.ActionDescriptor;
 5 
 6     IFilterMetadata[] filters;
 7     if (!cache.Entries.TryGetValue(actionDescriptor, out var cacheEntry))
 8     {
 9         var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, controllerContext);
10         filters = filterFactoryResult.Filters;
11 
12         var parameterDefaultValues = ParameterDefaultValues
13             .GetParameterDefaultValues(actionDescriptor.MethodInfo);
14 
15         var objectMethodExecutor = ObjectMethodExecutor.Create(
16             actionDescriptor.MethodInfo,
17             actionDescriptor.ControllerTypeInfo,
18             parameterDefaultValues);
19 
20         var controllerFactory = _controllerFactoryProvider.CreateControllerFactory(actionDescriptor);
21         var controllerReleaser = _controllerFactoryProvider.CreateControllerReleaser(actionDescriptor);
22         var propertyBinderFactory = ControllerBinderDelegateProvider.CreateBinderDelegate(
23             _parameterBinder,
24             _modelBinderFactory,
25             _modelMetadataProvider,
26             actionDescriptor,
27             _mvcOptions);
28 
29         var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
30 
31         cacheEntry = new ControllerActionInvokerCacheEntry(
32             filterFactoryResult.CacheableFilters,
33             controllerFactory,
34             controllerReleaser,
35             propertyBinderFactory,
36             objectMethodExecutor,
37             actionMethodExecutor);
38         cacheEntry = cache.Entries.GetOrAdd(actionDescriptor, cacheEntry);
39     }
40     else
41     {
42         // Filter instances from statically defined filter descriptors + from filter providers
43         filters = FilterFactory.CreateUncachedFilters(_filterProviders, controllerContext, cacheEntry.CachedFilters);
44     }
45 
46     return (cacheEntry, filters);

           總的來看,本段內容主要是為了組裝cacheEntry和 filters兩個內容,而一個大的 if 體現出這里加入了緩存機制,使系統不必每次都去拼湊這些,提高執行效率。

⑥IFilterMetadata[] filters,它是一個filter的集和,首先調用FilterFactory的GetAllFilters(_filterProviders, controllerContext)方法獲取當前action對應的所有Filter並對這些Filter進行排序(Filter部分將在之后章節分享)。

接下來就是組裝⑦cacheEntry,它的內容比較多,比較重要的幾個有:⑧ controllerFactory和controllerReleaser他們的本質都是Func<ControllerContext, object>,也就是Controller的Create和Release方法。 ⑨propertyBinderFactory 是一個用於參數綁定的Task,可以說也是一個組裝好准備被執行的方法。最后一個⑩actionMethodExecutor也就是執行者,通過ActionMethodExecutor.GetExecutor(objectMethodExecutor)方法從眾多的action執行者(如圖二)中找出一個當前action對應的執行者出來。

                                                             圖二

總結: 本節invoker的生成,總的來說就是一個執行前“萬事俱備”的過程,invoker是一個組裝起來的集合,它包含一個人(執行者actionMethodExecutor)、N把槍(組裝好用於“被執行”的方法例如controllerFactory、controllerReleaser和propertyBinderFactory,當然還有個filter的集和)。由此也可以進一步想到,接下來的過程就是這些准備好的內容按照一定的順序逐步執行的過程。

 二、invoker的執行

invoker的執行也就是invoker.InvokeAsync(),雖然invoker本質上是ControllerActionInvoker,但這個方法寫在ResourceInvoker類中, ControllerActionInvoker : ResourceInvoker, IActionInvoker 。

public virtual async Task InvokeAsync()
{
    try
    {
        await InvokeFilterPipelineAsync();
    }
    finally
    {
        ReleaseResources();
        _logger.ExecutedAction(_actionContext.ActionDescriptor, stopwatch.GetElapsedTime());
    }
}

private async Task InvokeFilterPipelineAsync()
{
    var next = State.InvokeBegin;
    var scope = Scope.Invoker;
    var state = (object)null;

    // `isCompleted` will be set to true when we've reached a terminal state.
    var isCompleted = false;

    while (!isCompleted)
    {
        await Next(ref next, ref scope, ref state, ref isCompleted);
    }
}

看似比較簡單的兩個方法,從InvokeAsync方法中可以看出來,請求會進入篩選器管道進行處理,也就是 Task InvokeFilterPipelineAsync() 方法,借用官方文檔中的一個圖看一下

                                  圖三

此圖描述了請求經過其他中間件處理后,進入路由處理最終找到了對應的action,最終進入篩選器管道進行處理。而這個處理的核心部分就是方法中的 while (!isCompleted) 循環,它對應的Next方法比較長,如下(較長已折疊)

  1         private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
  2         {
  3             switch (next)
  4             {
  5                 case State.InvokeBegin:
  6                     {
  7                         goto case State.AuthorizationBegin;
  8                     }
  9 
 10                 case State.AuthorizationBegin:
 11                     {
 12                         _cursor.Reset();
 13                         goto case State.AuthorizationNext;
 14                     }
 15 
 16                 case State.AuthorizationNext:
 17                     {
 18                         var current = _cursor.GetNextFilter<IAuthorizationFilter, IAsyncAuthorizationFilter>();
 19                         if (current.FilterAsync != null)
 20                         {
 21                             if (_authorizationContext == null)
 22                             {
 23                                 _authorizationContext = new AuthorizationFilterContext(_actionContext, _filters);
 24                             }
 25 
 26                             state = current.FilterAsync;
 27                             goto case State.AuthorizationAsyncBegin;
 28                         }
 29                         else if (current.Filter != null)
 30                         {
 31                             if (_authorizationContext == null)
 32                             {
 33                                 _authorizationContext = new AuthorizationFilterContext(_actionContext, _filters);
 34                             }
 35 
 36                             state = current.Filter;
 37                             goto case State.AuthorizationSync;
 38                         }
 39                         else
 40                         {
 41                             goto case State.AuthorizationEnd;
 42                         }
 43                     }
 44 
 45                 case State.AuthorizationAsyncBegin:
 46                     {
 47                         Debug.Assert(state != null);
 48                         Debug.Assert(_authorizationContext != null);
 49 
 50                         var filter = (IAsyncAuthorizationFilter)state;
 51                         var authorizationContext = _authorizationContext;
 52 
 53                         _diagnosticSource.BeforeOnAuthorizationAsync(authorizationContext, filter);
 54                         _logger.BeforeExecutingMethodOnFilter(
 55                             FilterTypeConstants.AuthorizationFilter,
 56                             nameof(IAsyncAuthorizationFilter.OnAuthorizationAsync),
 57                             filter);
 58 
 59                         var task = filter.OnAuthorizationAsync(authorizationContext);
 60                         if (task.Status != TaskStatus.RanToCompletion)
 61                         {
 62                             next = State.AuthorizationAsyncEnd;
 63                             return task;
 64                         }
 65 
 66                         goto case State.AuthorizationAsyncEnd;
 67                     }
 68 
 69                 case State.AuthorizationAsyncEnd:
 70                     {
 71                         Debug.Assert(state != null);
 72                         Debug.Assert(_authorizationContext != null);
 73 
 74                         var filter = (IAsyncAuthorizationFilter)state;
 75                         var authorizationContext = _authorizationContext;
 76 
 77                         _diagnosticSource.AfterOnAuthorizationAsync(authorizationContext, filter);
 78                         _logger.AfterExecutingMethodOnFilter(
 79                             FilterTypeConstants.AuthorizationFilter,
 80                             nameof(IAsyncAuthorizationFilter.OnAuthorizationAsync),
 81                             filter);
 82 
 83                         if (authorizationContext.Result != null)
 84                         {
 85                             goto case State.AuthorizationShortCircuit;
 86                         }
 87 
 88                         goto case State.AuthorizationNext;
 89                     }
 90 
 91                 case State.AuthorizationSync:
 92                     {
 93                         Debug.Assert(state != null);
 94                         Debug.Assert(_authorizationContext != null);
 95 
 96                         var filter = (IAuthorizationFilter)state;
 97                         var authorizationContext = _authorizationContext;
 98 
 99                         _diagnosticSource.BeforeOnAuthorization(authorizationContext, filter);
100                         _logger.BeforeExecutingMethodOnFilter(
101                             FilterTypeConstants.AuthorizationFilter,
102                             nameof(IAuthorizationFilter.OnAuthorization),
103                             filter);
104 
105                         filter.OnAuthorization(authorizationContext);
106 
107                         _diagnosticSource.AfterOnAuthorization(authorizationContext, filter);
108                         _logger.AfterExecutingMethodOnFilter(
109                             FilterTypeConstants.AuthorizationFilter,
110                             nameof(IAuthorizationFilter.OnAuthorization),
111                             filter);
112 
113                         if (authorizationContext.Result != null)
114                         {
115                             goto case State.AuthorizationShortCircuit;
116                         }
117 
118                         goto case State.AuthorizationNext;
119                     }
120 
121                 case State.AuthorizationShortCircuit:
122                     {
123                         Debug.Assert(state != null);
124                         Debug.Assert(_authorizationContext != null);
125                         Debug.Assert(_authorizationContext.Result != null);
126 
127                         _logger.AuthorizationFailure((IFilterMetadata)state);
128 
129                         // This is a short-circuit - execute relevant result filters + result and complete this invocation.
130                         isCompleted = true;
131                         _result = _authorizationContext.Result;
132                         return InvokeAlwaysRunResultFilters();
133                     }
134 
135                 case State.AuthorizationEnd:
136                     {
137                         goto case State.ResourceBegin;
138                     }
139 
140                 case State.ResourceBegin:
141                     {
142                         _cursor.Reset();
143                         goto case State.ResourceNext;
144                     }
145 
146                 case State.ResourceNext:
147                     {
148                         var current = _cursor.GetNextFilter<IResourceFilter, IAsyncResourceFilter>();
149                         if (current.FilterAsync != null)
150                         {
151                             if (_resourceExecutingContext == null)
152                             {
153                                 _resourceExecutingContext = new ResourceExecutingContext(
154                                     _actionContext,
155                                     _filters,
156                                     _valueProviderFactories);
157                             }
158 
159                             state = current.FilterAsync;
160                             goto case State.ResourceAsyncBegin;
161                         }
162                         else if (current.Filter != null)
163                         {
164                             if (_resourceExecutingContext == null)
165                             {
166                                 _resourceExecutingContext = new ResourceExecutingContext(
167                                     _actionContext,
168                                     _filters,
169                                     _valueProviderFactories);
170                             }
171 
172                             state = current.Filter;
173                             goto case State.ResourceSyncBegin;
174                         }
175                         else
176                         {
177                             // All resource filters are currently on the stack - now execute the 'inside'.
178                             goto case State.ResourceInside;
179                         }
180                     }
181 
182                 case State.ResourceAsyncBegin:
183                     {
184                         Debug.Assert(state != null);
185                         Debug.Assert(_resourceExecutingContext != null);
186 
187                         var filter = (IAsyncResourceFilter)state;
188                         var resourceExecutingContext = _resourceExecutingContext;
189 
190                         _diagnosticSource.BeforeOnResourceExecution(resourceExecutingContext, filter);
191                         _logger.BeforeExecutingMethodOnFilter(
192                             FilterTypeConstants.ResourceFilter,
193                             nameof(IAsyncResourceFilter.OnResourceExecutionAsync),
194                             filter);
195 
196                         var task = filter.OnResourceExecutionAsync(resourceExecutingContext, InvokeNextResourceFilterAwaitedAsync);
197                         if (task.Status != TaskStatus.RanToCompletion)
198                         {
199                             next = State.ResourceAsyncEnd;
200                             return task;
201                         }
202 
203                         goto case State.ResourceAsyncEnd;
204                     }
205 
206                 case State.ResourceAsyncEnd:
207                     {
208                         Debug.Assert(state != null);
209                         Debug.Assert(_resourceExecutingContext != null);
210 
211                         var filter = (IAsyncResourceFilter)state;
212                         if (_resourceExecutedContext == null)
213                         {
214                             // If we get here then the filter didn't call 'next' indicating a short circuit.
215                             _resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
216                             {
217                                 Canceled = true,
218                                 Result = _resourceExecutingContext.Result,
219                             };
220 
221                             _diagnosticSource.AfterOnResourceExecution(_resourceExecutedContext, filter);
222                             _logger.AfterExecutingMethodOnFilter(
223                                 FilterTypeConstants.ResourceFilter,
224                                 nameof(IAsyncResourceFilter.OnResourceExecutionAsync),
225                                 filter);
226 
227                             // A filter could complete a Task without setting a result
228                             if (_resourceExecutingContext.Result != null)
229                             {
230                                 goto case State.ResourceShortCircuit;
231                             }
232                         }
233 
234                         goto case State.ResourceEnd;
235                     }
236 
237                 case State.ResourceSyncBegin:
238                     {
239                         Debug.Assert(state != null);
240                         Debug.Assert(_resourceExecutingContext != null);
241 
242                         var filter = (IResourceFilter)state;
243                         var resourceExecutingContext = _resourceExecutingContext;
244 
245                         _diagnosticSource.BeforeOnResourceExecuting(resourceExecutingContext, filter);
246                         _logger.BeforeExecutingMethodOnFilter(
247                             FilterTypeConstants.ResourceFilter,
248                             nameof(IResourceFilter.OnResourceExecuting),
249                             filter);
250 
251                         filter.OnResourceExecuting(resourceExecutingContext);
252 
253                         _diagnosticSource.AfterOnResourceExecuting(resourceExecutingContext, filter);
254                         _logger.AfterExecutingMethodOnFilter(
255                             FilterTypeConstants.ResourceFilter,
256                             nameof(IResourceFilter.OnResourceExecuting),
257                             filter);
258 
259                         if (resourceExecutingContext.Result != null)
260                         {
261                             _resourceExecutedContext = new ResourceExecutedContext(resourceExecutingContext, _filters)
262                             {
263                                 Canceled = true,
264                                 Result = _resourceExecutingContext.Result,
265                             };
266 
267                             goto case State.ResourceShortCircuit;
268                         }
269 
270                         var task = InvokeNextResourceFilter();
271                         if (task.Status != TaskStatus.RanToCompletion)
272                         {
273                             next = State.ResourceSyncEnd;
274                             return task;
275                         }
276 
277                         goto case State.ResourceSyncEnd;
278                     }
279 
280                 case State.ResourceSyncEnd:
281                     {
282                         Debug.Assert(state != null);
283                         Debug.Assert(_resourceExecutingContext != null);
284                         Debug.Assert(_resourceExecutedContext != null);
285 
286                         var filter = (IResourceFilter)state;
287                         var resourceExecutedContext = _resourceExecutedContext;
288 
289                         _diagnosticSource.BeforeOnResourceExecuted(resourceExecutedContext, filter);
290                         _logger.BeforeExecutingMethodOnFilter(
291                             FilterTypeConstants.ResourceFilter,
292                             nameof(IResourceFilter.OnResourceExecuted),
293                             filter);
294 
295                         filter.OnResourceExecuted(resourceExecutedContext);
296 
297                         _diagnosticSource.AfterOnResourceExecuted(resourceExecutedContext, filter);
298                         _logger.AfterExecutingMethodOnFilter(
299                             FilterTypeConstants.ResourceFilter,
300                             nameof(IResourceFilter.OnResourceExecuted),
301                             filter);
302 
303                         goto case State.ResourceEnd;
304                     }
305 
306                 case State.ResourceShortCircuit:
307                     {
308                         Debug.Assert(state != null);
309                         Debug.Assert(_resourceExecutingContext != null);
310                         Debug.Assert(_resourceExecutedContext != null);
311 
312                         _logger.ResourceFilterShortCircuited((IFilterMetadata)state);
313 
314                         _result = _resourceExecutingContext.Result;
315                         var task = InvokeAlwaysRunResultFilters();
316                         if (task.Status != TaskStatus.RanToCompletion)
317                         {
318                             next = State.ResourceEnd;
319                             return task;
320                         }
321 
322                         goto case State.ResourceEnd;
323                     }
324 
325                 case State.ResourceInside:
326                     {
327                         goto case State.ExceptionBegin;
328                     }
329 
330                 case State.ExceptionBegin:
331                     {
332                         _cursor.Reset();
333                         goto case State.ExceptionNext;
334                     }
335 
336                 case State.ExceptionNext:
337                     {
338                         var current = _cursor.GetNextFilter<IExceptionFilter, IAsyncExceptionFilter>();
339                         if (current.FilterAsync != null)
340                         {
341                             state = current.FilterAsync;
342                             goto case State.ExceptionAsyncBegin;
343                         }
344                         else if (current.Filter != null)
345                         {
346                             state = current.Filter;
347                             goto case State.ExceptionSyncBegin;
348                         }
349                         else if (scope == Scope.Exception)
350                         {
351                             // All exception filters are on the stack already - so execute the 'inside'.
352                             goto case State.ExceptionInside;
353                         }
354                         else
355                         {
356                             // There are no exception filters - so jump right to the action.
357                             Debug.Assert(scope == Scope.Invoker || scope == Scope.Resource);
358                             goto case State.ActionBegin;
359                         }
360                     }
361 
362                 case State.ExceptionAsyncBegin:
363                     {
364                         var task = InvokeNextExceptionFilterAsync();
365                         if (task.Status != TaskStatus.RanToCompletion)
366                         {
367                             next = State.ExceptionAsyncResume;
368                             return task;
369                         }
370 
371                         goto case State.ExceptionAsyncResume;
372                     }
373 
374                 case State.ExceptionAsyncResume:
375                     {
376                         Debug.Assert(state != null);
377 
378                         var filter = (IAsyncExceptionFilter)state;
379                         var exceptionContext = _exceptionContext;
380 
381                         // When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception,
382                         // we'll call the filter. Otherwise there's nothing to do.
383                         if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled)
384                         {
385                             _diagnosticSource.BeforeOnExceptionAsync(exceptionContext, filter);
386                             _logger.BeforeExecutingMethodOnFilter(
387                                 FilterTypeConstants.ExceptionFilter,
388                                 nameof(IAsyncExceptionFilter.OnExceptionAsync),
389                                 filter);
390 
391                             var task = filter.OnExceptionAsync(exceptionContext);
392                             if (task.Status != TaskStatus.RanToCompletion)
393                             {
394                                 next = State.ExceptionAsyncEnd;
395                                 return task;
396                             }
397 
398                             goto case State.ExceptionAsyncEnd;
399                         }
400 
401                         goto case State.ExceptionEnd;
402                     }
403 
404                 case State.ExceptionAsyncEnd:
405                     {
406                         Debug.Assert(state != null);
407                         Debug.Assert(_exceptionContext != null);
408 
409                         var filter = (IAsyncExceptionFilter)state;
410                         var exceptionContext = _exceptionContext;
411 
412                         _diagnosticSource.AfterOnExceptionAsync(exceptionContext, filter);
413                         _logger.AfterExecutingMethodOnFilter(
414                             FilterTypeConstants.ExceptionFilter,
415                             nameof(IAsyncExceptionFilter.OnExceptionAsync),
416                             filter);
417 
418                         if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled)
419                         {
420                             // We don't need to do anything to trigger a short circuit. If there's another
421                             // exception filter on the stack it will check the same set of conditions
422                             // and then just skip itself.
423                             _logger.ExceptionFilterShortCircuited(filter);
424                         }
425 
426                         goto case State.ExceptionEnd;
427                     }
428 
429                 case State.ExceptionSyncBegin:
430                     {
431                         var task = InvokeNextExceptionFilterAsync();
432                         if (task.Status != TaskStatus.RanToCompletion)
433                         {
434                             next = State.ExceptionSyncEnd;
435                             return task;
436                         }
437 
438                         goto case State.ExceptionSyncEnd;
439                     }
440 
441                 case State.ExceptionSyncEnd:
442                     {
443                         Debug.Assert(state != null);
444 
445                         var filter = (IExceptionFilter)state;
446                         var exceptionContext = _exceptionContext;
447 
448                         // When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception,
449                         // we'll call the filter. Otherwise there's nothing to do.
450                         if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled)
451                         {
452                             _diagnosticSource.BeforeOnException(exceptionContext, filter);
453                             _logger.BeforeExecutingMethodOnFilter(
454                                 FilterTypeConstants.ExceptionFilter,
455                                 nameof(IExceptionFilter.OnException),
456                                 filter);
457 
458                             filter.OnException(exceptionContext);
459 
460                             _diagnosticSource.AfterOnException(exceptionContext, filter);
461                             _logger.AfterExecutingMethodOnFilter(
462                                 FilterTypeConstants.ExceptionFilter,
463                                 nameof(IExceptionFilter.OnException),
464                                 filter);
465 
466                             if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled)
467                             {
468                                 // We don't need to do anything to trigger a short circuit. If there's another
469                                 // exception filter on the stack it will check the same set of conditions
470                                 // and then just skip itself.
471                                 _logger.ExceptionFilterShortCircuited(filter);
472                             }
473                         }
474 
475                         goto case State.ExceptionEnd;
476                     }
477 
478                 case State.ExceptionInside:
479                     {
480                         goto case State.ActionBegin;
481                     }
482 
483                 case State.ExceptionHandled:
484                     {
485                         // We arrive in this state when an exception happened, but was handled by exception filters
486                         // either by setting ExceptionHandled, or nulling out the Exception or setting a result
487                         // on the ExceptionContext.
488                         //
489                         // We need to execute the result (if any) and then exit gracefully which unwinding Resource 
490                         // filters.
491 
492                         Debug.Assert(state != null);
493                         Debug.Assert(_exceptionContext != null);
494 
495                         if (_exceptionContext.Result == null)
496                         {
497                             _exceptionContext.Result = new EmptyResult();
498                         }
499 
500                         _result = _exceptionContext.Result;
501 
502                         var task = InvokeAlwaysRunResultFilters();
503                         if (task.Status != TaskStatus.RanToCompletion)
504                         {
505                             next = State.ResourceInsideEnd;
506                             return task;
507                         }
508 
509                         goto case State.ResourceInsideEnd;
510                     }
511 
512                 case State.ExceptionEnd:
513                     {
514                         var exceptionContext = _exceptionContext;
515 
516                         if (scope == Scope.Exception)
517                         {
518                             isCompleted = true;
519                             return Task.CompletedTask;
520                         }
521 
522                         if (exceptionContext != null)
523                         {
524                             if (exceptionContext.Result != null ||
525                                 exceptionContext.Exception == null ||
526                                 exceptionContext.ExceptionHandled)
527                             {
528                                 goto case State.ExceptionHandled;
529                             }
530 
531                             Rethrow(exceptionContext);
532                             Debug.Fail("unreachable");
533                         }
534 
535                         var task = InvokeResultFilters();
536                         if (task.Status != TaskStatus.RanToCompletion)
537                         {
538                             next = State.ResourceInsideEnd;
539                             return task;
540                         }
541                         goto case State.ResourceInsideEnd;
542                     }
543 
544                 case State.ActionBegin:
545                     {
546                         var task = InvokeInnerFilterAsync();
547                         if (task.Status != TaskStatus.RanToCompletion)
548                         {
549                             next = State.ActionEnd;
550                             return task;
551                         }
552 
553                         goto case State.ActionEnd;
554                     }
555 
556                 case State.ActionEnd:
557                     {
558                         if (scope == Scope.Exception)
559                         {
560                             // If we're inside an exception filter, let's allow those filters to 'unwind' before
561                             // the result.
562                             isCompleted = true;
563                             return Task.CompletedTask;
564                         }
565 
566                         Debug.Assert(scope == Scope.Invoker || scope == Scope.Resource);
567                         var task = InvokeResultFilters();
568                         if (task.Status != TaskStatus.RanToCompletion)
569                         {
570                             next = State.ResourceInsideEnd;
571                             return task;
572                         }
573                         goto case State.ResourceInsideEnd;
574                     }
575 
576                 case State.ResourceInsideEnd:
577                     {
578                         if (scope == Scope.Resource)
579                         {
580                             _resourceExecutedContext = new ResourceExecutedContext(_actionContext, _filters)
581                             {
582                                 Result = _result,
583                             };
584 
585                             goto case State.ResourceEnd;
586                         }
587 
588                         goto case State.InvokeEnd;
589                     }
590 
591                 case State.ResourceEnd:
592                     {
593                         if (scope == Scope.Resource)
594                         {
595                             isCompleted = true;
596                             return Task.CompletedTask;
597                         }
598 
599                         Debug.Assert(scope == Scope.Invoker);
600                         Rethrow(_resourceExecutedContext);
601 
602                         goto case State.InvokeEnd;
603                     }
604 
605                 case State.InvokeEnd:
606                     {
607                         isCompleted = true;
608                         return Task.CompletedTask;
609                     }
610 
611                 default:
612                     throw new InvalidOperationException();
613             }
614         }
View Code

從代碼可以看出,它是根據狀態State進行輪轉,而執行順序是Authorization->Resource->Exception......  也就是說當前action對應的多種類型的Filter會按照這樣的順序被執行,如下圖

            圖四

可以看出,在上面幾個Filter執行之后,ActionFilter的執行比較特殊,它將Action的執行包在了中間,這段邏輯寫在了ControllerActionInvoker自己的類中,同樣是一個 Task Next 方法被while循環調用,如下

  1 private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
  2         {
  3             switch (next)
  4             {
  5                 case State.ActionBegin:
  6                     {
  7                         var controllerContext = _controllerContext;
  8 
  9                         _cursor.Reset();
 10 
 11                         _instance = _cacheEntry.ControllerFactory(controllerContext);
 12 
 13                         _arguments = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
 14 
 15                         var task = BindArgumentsAsync();
 16                         if (task.Status != TaskStatus.RanToCompletion)
 17                         {
 18                             next = State.ActionNext;
 19                             return task;
 20                         }
 21 
 22                         goto case State.ActionNext;
 23                     }
 24 
 25                 case State.ActionNext:
 26                     {
 27                         var current = _cursor.GetNextFilter<IActionFilter, IAsyncActionFilter>();
 28                         if (current.FilterAsync != null)
 29                         {
 30                             if (_actionExecutingContext == null)
 31                             {
 32                                 _actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, _arguments, _instance);
 33                             }
 34 
 35                             state = current.FilterAsync;
 36                             goto case State.ActionAsyncBegin;
 37                         }
 38                         else if (current.Filter != null)
 39                         {
 40                             if (_actionExecutingContext == null)
 41                             {
 42                                 _actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, _arguments, _instance);
 43                             }
 44 
 45                             state = current.Filter;
 46                             goto case State.ActionSyncBegin;
 47                         }
 48                         else
 49                         {
 50                             goto case State.ActionInside;
 51                         }
 52                     }
 53 
 54                 case State.ActionAsyncBegin:
 55                     {
 56                         Debug.Assert(state != null);
 57                         Debug.Assert(_actionExecutingContext != null);
 58 
 59                         var filter = (IAsyncActionFilter)state;
 60                         var actionExecutingContext = _actionExecutingContext;
 61 
 62                         _diagnosticSource.BeforeOnActionExecution(actionExecutingContext, filter);
 63                         _logger.BeforeExecutingMethodOnFilter(
 64                             MvcCoreLoggerExtensions.ActionFilter,
 65                             nameof(IAsyncActionFilter.OnActionExecutionAsync),
 66                             filter);
 67 
 68                         var task = filter.OnActionExecutionAsync(actionExecutingContext, InvokeNextActionFilterAwaitedAsync);
 69                         if (task.Status != TaskStatus.RanToCompletion)
 70                         {
 71                             next = State.ActionAsyncEnd;
 72                             return task;
 73                         }
 74 
 75                         goto case State.ActionAsyncEnd;
 76                     }
 77 
 78                 case State.ActionAsyncEnd:
 79                     {
 80                         Debug.Assert(state != null);
 81                         Debug.Assert(_actionExecutingContext != null);
 82 
 83                         var filter = (IAsyncActionFilter)state;
 84 
 85                         if (_actionExecutedContext == null)
 86                         {
 87                             // If we get here then the filter didn't call 'next' indicating a short circuit.
 88                             _logger.ActionFilterShortCircuited(filter);
 89 
 90                             _actionExecutedContext = new ActionExecutedContext(
 91                                 _controllerContext,
 92                                 _filters,
 93                                 _instance)
 94                             {
 95                                 Canceled = true,
 96                                 Result = _actionExecutingContext.Result,
 97                             };
 98                         }
 99 
100                         _diagnosticSource.AfterOnActionExecution(_actionExecutedContext, filter);
101                         _logger.AfterExecutingMethodOnFilter(
102                             MvcCoreLoggerExtensions.ActionFilter,
103                             nameof(IAsyncActionFilter.OnActionExecutionAsync),
104                             filter);
105 
106                         goto case State.ActionEnd;
107                     }
108 
109                 case State.ActionSyncBegin:
110                     {
111                         Debug.Assert(state != null);
112                         Debug.Assert(_actionExecutingContext != null);
113 
114                         var filter = (IActionFilter)state;
115                         var actionExecutingContext = _actionExecutingContext;
116 
117                         _diagnosticSource.BeforeOnActionExecuting(actionExecutingContext, filter);
118                         _logger.BeforeExecutingMethodOnFilter(
119                             MvcCoreLoggerExtensions.ActionFilter,
120                             nameof(IActionFilter.OnActionExecuting),
121                             filter);
122 
123                         filter.OnActionExecuting(actionExecutingContext);
124 
125                         _diagnosticSource.AfterOnActionExecuting(actionExecutingContext, filter);
126                         _logger.AfterExecutingMethodOnFilter(
127                             MvcCoreLoggerExtensions.ActionFilter,
128                             nameof(IActionFilter.OnActionExecuting),
129                             filter);
130 
131                         if (actionExecutingContext.Result != null)
132                         {
133                             // Short-circuited by setting a result.
134                             _logger.ActionFilterShortCircuited(filter);
135 
136                             _actionExecutedContext = new ActionExecutedContext(
137                                 _actionExecutingContext,
138                                 _filters,
139                                 _instance)
140                             {
141                                 Canceled = true,
142                                 Result = _actionExecutingContext.Result,
143                             };
144 
145                             goto case State.ActionEnd;
146                         }
147 
148                         var task = InvokeNextActionFilterAsync();
149                         if (task.Status != TaskStatus.RanToCompletion)
150                         {
151                             next = State.ActionSyncEnd;
152                             return task;
153                         }
154 
155                         goto case State.ActionSyncEnd;
156                     }
157 
158                 case State.ActionSyncEnd:
159                     {
160                         Debug.Assert(state != null);
161                         Debug.Assert(_actionExecutingContext != null);
162                         Debug.Assert(_actionExecutedContext != null);
163 
164                         var filter = (IActionFilter)state;
165                         var actionExecutedContext = _actionExecutedContext;
166 
167                         _diagnosticSource.BeforeOnActionExecuted(actionExecutedContext, filter);
168                         _logger.BeforeExecutingMethodOnFilter(
169                             MvcCoreLoggerExtensions.ActionFilter,
170                             nameof(IActionFilter.OnActionExecuted),
171                             filter);
172 
173                         filter.OnActionExecuted(actionExecutedContext);
174 
175                         _diagnosticSource.AfterOnActionExecuted(actionExecutedContext, filter);
176                         _logger.AfterExecutingMethodOnFilter(
177                             MvcCoreLoggerExtensions.ActionFilter,
178                             nameof(IActionFilter.OnActionExecuted),
179                             filter);
180 
181                         goto case State.ActionEnd;
182                     }
183 
184                 case State.ActionInside:
185                     {
186                         var task = InvokeActionMethodAsync();
187                         if (task.Status != TaskStatus.RanToCompletion)
188                         {
189                             next = State.ActionEnd;
190                             return task;
191                         }
192 
193                         goto case State.ActionEnd;
194                     }
195 
196                 case State.ActionEnd:
197                     {
198                         if (scope == Scope.Action)
199                         {
200                             if (_actionExecutedContext == null)
201                             {
202                                 _actionExecutedContext = new ActionExecutedContext(_controllerContext, _filters, _instance)
203                                 {
204                                     Result = _result,
205                                 };
206                             }
207 
208                             isCompleted = true;
209                             return Task.CompletedTask;
210                         }
211 
212                         var actionExecutedContext = _actionExecutedContext;
213                         Rethrow(actionExecutedContext);
214 
215                         if (actionExecutedContext != null)
216                         {
217                             _result = actionExecutedContext.Result;
218                         }
219 
220                         isCompleted = true;
221                         return Task.CompletedTask;
222                     }
223 
224                 default:
225                     throw new InvalidOperationException();
226             }
227         }
View Code

而在ActionBegin的時候,通過ControllerFactory創建了Controller並調用 cacheEntry.ControllerBinderDelegate(_controllerContext, _instance, _arguments) 進行了參數綁定。

然后的順序是   ActionFilter的OnActionExecuting方法 ->action的執行->ActionFilter的OnActionExecuted方法, action的執行如下:

private async Task InvokeActionMethodAsync()
{
    var controllerContext = _controllerContext;
    var objectMethodExecutor = _cacheEntry.ObjectMethodExecutor;
    var controller = _instance;
    var arguments = _arguments;
    var actionMethodExecutor = _cacheEntry.ActionMethodExecutor;
    var orderedArguments = PrepareArguments(arguments, objectMethodExecutor);

    var diagnosticSource = _diagnosticSource;
    var logger = _logger;

    IActionResult result = null;
    try
    {
        diagnosticSource.BeforeActionMethod(
            controllerContext,
            arguments,
            controller);
        logger.ActionMethodExecuting(controllerContext, orderedArguments);
        var stopwatch = ValueStopwatch.StartNew();
        var actionResultValueTask = actionMethodExecutor.Execute(_mapper, objectMethodExecutor, controller, orderedArguments);
        if (actionResultValueTask.IsCompletedSuccessfully)
        {
            result = actionResultValueTask.Result;
        }
        else
        {
            result = await actionResultValueTask;
        }

        _result = result;
        logger.ActionMethodExecuted(controllerContext, result, stopwatch.GetElapsedTime());
    }
    finally
    {
        diagnosticSource.AfterActionMethod(
            controllerContext,
            arguments,
            controllerContext,
            result);
    }
}

 

總結: 如上文說的,本節的內容就是將准備階段組裝的多個方法在這里按一定的被逐步的執行(如圖四)。

 

大概內容就是這樣,詳細分析起來涉及細節還有好多,后面的文章會對一些關鍵內容進行詳細分享。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM