学习ASP.NET Core, 怎能不了解请求处理管道[5]: 中间件注册可以除了可以使用Startup之外,还可以选择StartupFilter

中间件的注册除了可以借助Startup对象(DelegateStartup或者ConventionBasedStartup)来完成之外,也可以利用另一个叫做StartupFilter的对象来实现。所谓的StartupFilter是对所有实现了IStartupFilter接口的类型及其对象的统称。IStartupFilter接口定义了如下一个唯一的方法Configure,该方法的参数next返回的Action<IApplicationBuilder>对象体现了后续StartupFilter和Startup对中间件的注册,而自身对中间件的注册则实现在返回的Action<IApplicationBuilder>对象中。[本文已经同步到《ASP.NET Core框架揭秘》之中]

1: public interface IStartupFilter   2: {   3:     Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);   4: }

我们可以采用服务注册的方式注册多个StartupFilter。具体来说,StartupFilter具有如下两种不同的注册方式,一种是通过调用WebHostBuilder的ConfigureServices方法以服务的形式注册所需的StartupFilter,另一种则是将针对StartupFilter的服务注册实现在启动类的ConfigureServices方法上。

1: //注册方式1   2: new WebHostBuilder()   3:     .ConfigureServices(svcs => svcs   4:         .AddSingleton<IStartupFilter, Filter1>()   5:         .AddSingleton<IStartupFilter, Filter2>())                       6:     …   7:     8: //注册方式2   9: public class Startup  10: {  11:     public void ConfigureServices(IServiceCollection svcs)  12:     {  13:         svcs.AddSingleton<IStartupFilter,Filter1>()  14:             .AddSingleton<IStartupFilter, Filter2>();  15:     }      16: }

既然中间件可以同时通过Startup和StartupFilter进行注册,那么通过这两个种方式注册的中间件有何不同吗?其实它们唯一的区别在于StartupFilter注册的中间件会先执行。话句话说,对于由注册中间件构成的管道来说,通过Startup注册的中间件位于通过StartupFilter注册的中间件之后。我们不妨通过一个简单的实例来证实这一点。我们在一个ASP.NET Core控制台应用中定义如下四个中间件类型(Foo、Bar、Baz和Gux),它们针对请求的处理逻辑很简单,就是将自身的类型名称写入请求的响应中。

1: public abstract class MiddlewareBase   2: {   3:     private RequestDelegate _next;   4:     5:     public MiddlewareBase(RequestDelegate next)   6:     {   7:         _next = next;   8:     }   9:     public async Task Invoke(HttpContext context)  10:     {  11:         await context.Response.WriteAsync($"{this.GetType().Name}=>");  12:         await _next(context);  13:     }  14: }  15:    16: public class Foo : MiddlewareBase  17: {  18:     public Foo(RequestDelegate next) : base(next){}  19: }  20: public class Bar : MiddlewareBase  21: {  22:     public Bar(RequestDelegate next) : base(next) {}  23: }  24: public class Baz : MiddlewareBase  25: {  26:     public Baz(RequestDelegate next) : base(next) {}  27: }  28: public class Gux : MiddlewareBase  29: {  30:     public Gux(RequestDelegate next) : base(next) {}  31: }

接下来我们定义了如下一个泛型的 StartupFilter<TMiddleware>类,这是一个专门用于注册指定类型中间件的StartupFilter,泛型参数代表注册的中间件类型。在实现的Configure方法中,我们将中间件的注册实现在返回的Action<IApplicationBuilder>对象中。

1: public class StartupFilter<TMiddleware> : IStartupFilter   2: {   3:     public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)   4:     {   5:         return app=> {   6:             app.UseMiddleware<TMiddleware>();   7:             next(app);   8:         };   9:     }  10: }

我们最终编写如下一段简单的程序来启动承载的应用程序。如下面的额代码片段所示,在利用WebHostBuilder创建并启动WebHost之前,我们调用其ConfigureServices方法注册了两个StartupFilter<TMiddleware>对象,它们对应的中间件类型分别为Foo和Bar。在随后调用的Configure方法中,我们又完成了针对中间Baz和Gux的注册。这段程序实际上注册了五个中间件(调用ApplicationBuilder的Run方法可以视为中间件注册)。

1: public class Program   2: {   3:     public static void Main()   4:     {   5:         new WebHostBuilder()   6:             .UseKestrel()   7:             .ConfigureServices(svcs => svcs   8:                 .AddSingleton<IStartupFilter>(new StartupFilter<Foo>())   9:                 .AddSingleton<IStartupFilter>(new StartupFilter<Bar>()))  10:             .Configure(app => app  11:                 .UseMiddleware<Baz>()  12:                 .UseMiddleware<Gux>()  13:                 .Run(async context=> await context.Response.WriteAsync("End")))  14:             .Build()  15:             .Run();  16:     }  17: }

我们现在需要确定注册的这五个在进行请求处理过程中的执行顺序。为此我们直接启动这个程序,然后开启浏览器访问默认的监听地址(http://localhost:5000),浏览器会按照如下图所示形式显示出请求在这个五个中间件中的“路由”。浏览器显示的结果清晰地表明通过StartupFilter注册的中间件比通过Startup注册的中间件先执行。对于两个采用相同方式注册的中间件,先被注册的中间会先执行。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python小屋

Python程序编译与反编译

众所周知,Python是纯粹的自由软件, 源代码和解释器CPython遵循 GPL(GNU General Public License)协议。那么很自然会有人...

6726
来自专栏zhangdd.com

tomcat8 启动报错: ignoring option PermSize=256m处理方法

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=512m; suppor...

1771
来自专栏orientlu

python 模块与包

每个文件夹对应一个包,包里面可以包含多个包,对应的每个 .py 文件是一个具体模块。 封装成包,在文件上组织好代码后,确保每个目录都要一个 __init__....

1483
来自专栏维C果糖

史上最简单的 MySQL 教程(七)「中文数据问题」

由于计算机仅识别二进制数据,而且人类则更倾向于识别字符(符号),因此就需要一个二进制与字符的对应关系,也就是字符集。

3815
来自专栏开源项目

Git 项目推荐 | javascript ajax 代理调用工具

javascript ajax 代理调用工具 。 AjaxProxy url: /template/default/script/AjaxProxy.js; 接...

3079
来自专栏耕耘实录

Linux中find命令的使用详解(下)

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢

2268
来自专栏PHP在线

拒绝重复造轮子,用composer搞自己的框架(2)

久负盛名的 CodeIgniter 框架是很多人的 PHP 开发入门框架,同样也是我开始学习如何从头构建一个网站的框架。在 CI中我学到了很多,其中对 MVC ...

3839
来自专栏php

Composer常见错误解决

执行composer install遇到错误:Your requirements could not be resolved to an installable...

3270
来自专栏栗霖积跬步之旅

git中的merge与rebase

2896
来自专栏架构师之路

2副图秒懂SQL中的join

废话不多说,直接上图秒懂。 t1表的结构与数据如下: ? t2表的结构与数据如下: ? inner join select * from t1 inner j...

3715

扫码关注云+社区

领取腾讯云代金券