理解ASP.NET Core 中间件(Middleware)(2)

public class Startup { public void Configure(IApplicationBuilder app) { // /get 或 /get/xxx 都会进入该管道分支 app.MapWhen(context => context.Request.Path.StartsWithSegments("/get"), app => { app.MapWhen(context => context.Request.Path.ToString().Contains("user"), app => { app.Use(async (context, next) => { Console.WriteLine("MapWhen get user: Use"); await next(); }); }); app.Use(async (context, next) => { Console.WriteLine("MapWhen get: Use"); await next(); }); app.Run(async context => { Console.WriteLine("MapWhen get: Run"); await context.Response.WriteAsync("Hello World!"); }); }); } }

当你访问 /get/user 时,输出如下:

MapWhen get user: Use

可以看到,即使该管道分支没有终端中间件,也不会回到主管道。

Run & Use & UseWhen & Map & Map

一下子接触了4个命名相似的、与中间件管道有关的API,不知道你有没有晕倒,没关系,我来帮大家总结一下:

Run用于注册终端中间件,Use用来注册匿名中间件,UseWhen、Map、MapWhen用于创建管道分支。

UseWhen进入管道分支后,如果管道分支中不存在短路或终端中间件,则会返回到主管道。Map和MapWhen进入管道分支后,无论如何,都不会再返回到主管道。

UseWhen和MapWhen基于逻辑条件来创建管道分支,而Map基于请求路径来创建管道分支,且会对HttpRequest.Path和HttpRequest.PathBase进行处理。

编写中间件并激活

上面已经提到过的Run和Use就不再赘述了。

基于约定的中间件

“约定大于配置”,先来个约法三章:

1.拥有公共(public)构造函数,且该构造函数至少包含一个类型为RequestDelegate的参数

2.拥有名为Invoke或InvokeAsync的公共(public)方法,必须包含一个类型为HttpContext的方法参数,且该参数必须位于第一个参数的位置,另外该方法必须返回Task类型。

3.构造函数中的其他参数可以通过依赖注入(DI)填充,也可以通过UseMiddleware传参进行填充。

通过DI填充时,只能接收 Transient 和 Singleton 的DI参数。这是由于中间件是在应用启动时构造的(而不是按请求构造),所以当出现 Scoped 参数时,构造函数内的DI参数生命周期与其他不共享,如果想要共享,则必须将Scoped DI参数添加到Invoke/InvokeAsync来进行使用。

通过UseMiddleware传参时,构造函数内的DI参数和非DI参数顺序没有要求,传入UseMiddleware内的参数顺序也没有要求,但是我建议将非DI参数放到前面,DI参数放到后面。(这一块感觉微软做的好牛皮)

4.Invoke/InvokeAsync的其他参数也能够通过依赖注入(DI)填充,可以接收 Transient、Scoped 和 Singleton 的DI参数。

一个简单的中间件如下:

public class MyMiddleware { // 用于调用管道中的下一个中间件 private readonly RequestDelegate _next; public MyMiddleware( RequestDelegate next, ITransientService transientService, ISingletonService singletonService) { _next = next; } public async Task InvokeAsync( HttpContext context, ITransientService transientService, IScopedService scopedService, ISingletonService singletonService) { // 下一个中间件处理之前的操作 Console.WriteLine("MyMiddleware Begin"); await _next(context); // 下一个中间件处理完成后的操作 Console.WriteLine("MyMiddleware End"); } }

然后,你可以通过UseMiddleware方法将其添加到管道中

public class Startup { public void Configure(IApplicationBuilder app) { app.UseMiddleware<MyMiddleware>(); } }

不过,一般不推荐直接使用UseMiddleware,而是将其封装到扩展方法中

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysfdd.html