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

简介: 上一章介绍了经过路由的处理,一个请求找到了具体处理这个请求的EndPoint,并最终执行它的RequestDelegate方法来处理这个Httpcontext。本章继续这个处理进程,按照惯例,依然通过几幅图来聊一聊这个RequestDelegate之后的故事。在此就避免不了的聊到各种Filter,它方便我们在action执行的前后做一些 “小动作”。

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对应的执行者出来。

5.png

   图二


总结: 本节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() 方法,借用官方文档中的一个图看一下

6.png

 图三

此图描述了请求经过其他中间件处理后,进入路由处理最终找到了对应的action,最终进入筛选器管道进行处理。而这个处理的核心部分就是方法中的 while (!isCompleted) 循环,它对应的Next方法比较长,如下(较长已折叠)


目录
相关文章
|
17天前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
3月前
|
开发框架 前端开发 JavaScript
盘点72个ASP.NET Core源码Net爱好者不容错过
盘点72个ASP.NET Core源码Net爱好者不容错过
71 0
|
3月前
|
开发框架 .NET
ASP.NET Core NET7 增加session的方法
ASP.NET Core NET7 增加session的方法
37 0
|
1月前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(持续更新)
C#/.NET/.NET Core拾遗补漏合集(持续更新)
|
1月前
|
开发框架 中间件 .NET
C# .NET面试系列七:ASP.NET Core
## 第一部分:ASP.NET Core #### 1. 如何在 controller 中注入 service? 在.NET中,在ASP.NET Core应用程序中的Controller中注入服务通常使用<u>依赖注入(Dependency Injection)</u>来实现。以下是一些步骤,说明如何在Controller中注入服务: 1、创建服务 首先,确保你已经在应用程序中注册了服务。这通常在Startup.cs文件的ConfigureServices方法中完成。例如: ```c# services.AddScoped<IMyService, MyService>(); //
63 0
|
2月前
|
开发框架 前端开发 .NET
福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!
为了便于大家查找,特将之前开发的.Net Core相关的五大案例整理成文,共计440页,32w字,免费提供给大家,文章底部有PDF下载链接。
35 1
福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!
|
2月前
|
算法 BI API
C#/.NET/.NET Core优秀项目和框架2024年1月简报
C#/.NET/.NET Core优秀项目和框架2024年1月简报
|
前端开发 .NET Linux
|
前端开发 .NET Linux
【翻译】Asp.net Core介绍
ASP.NET Core is a significant redesign of ASP.NET. This topic introduces the new concepts in ASP.NET Core and explains how they help you develop modern web apps. Asp.net Core是重新设计过得新一代Asp.Net。
1154 0
|
3月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
42 0