中间件
- 广义: ASP.NETCore中的中间件指ASP.NETCore中的一个组件。- 组成部分: 中间件由前逻辑、next、后逻辑3部分组成,前逻辑为第一段要执行的逻辑代码、next为指向下一个中间件的调用、 后逻辑为从下一个中间件执行返回所执行的逻辑代码。每个HTTP请求都要经历一系列中间件的处理,每个中间件对于请求进行特 定的处理后,再转到下一个中间件,最终的业务逻辑代码执行完成后,响应的内容也会按照处理的相反顺序进行处理,然后形成 HTTP响应报文返回给客户端。- 中间件组成一个管道,整个ASP.NETCore的执行过程就是HTTP请求和响应按照中间件组装的顺序在中间件之间流转的过程。开发人员可以对组成管道的中间件按照需要进行自由组合。
- 中间件的三个概念:
Map
,Use
,Run
- Map用来定义一个管道可以处理哪些请求
- Use和Run用来定义管道,一个管道由若干个Use和一个Run组成,每个Use引入一个中间件,而Run是用来执行最终的核心应用逻辑。
- 我们创建一个
空asp.net项目
,从一无所有
开始搭建中间件
// Program.cs(很简单的代码,跑起来只有一个Hello World!)var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();app.MapGet("/", () => "Hello World!");app.Run();- 运行: https://localhost:7118/- 我们可以小改一下,返回简单字符
......
app.MapGet("/", () => "Hello World!");
app.MapGet("/test", () => "你若xxxx");
......
- 来一个简单示例
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();app.MapGet("/", () => "Hello World!");
//app.MapGet("/test", () => "你若xxxx");app.Map("/test", async (pipeBuilder) =>
{pipeBuilder.Use(async (context, next) =>{context.Response.ContentType = "text/html";await context.Response.WriteAsync("1 start<br/>");await next.Invoke();await context.Response.WriteAsync("1 end<br/>");});pipeBuilder.Use(async (context, next) =>{await context.Response.WriteAsync("2 start<br/>");await next.Invoke();await context.Response.WriteAsync("2 end<br/>");});pipeBuilder.Use(async (context, next) =>{await context.Response.WriteAsync("3 start<br/>");await next.Invoke();await context.Response.WriteAsync("3 end<br/>");});pipeBuilder.Run(async context =>{await context.Response.WriteAsync("Run<br/>");});});app.Run();
自定义中间件管道(重点)
app.Map("/test", async (pipeBuilder) =>
{// 创建自定义的中间件管道
});
中间件管道详细解析
管道执行顺序:
请求 → 中间件1 → 中间件2 → 中间件3 → Run终端 → 中间件3 → 中间件2 → 中间件1 → 响应
三个中间件的执行流程:
pipeBuilder.Use(async (context, next) =>
{context.Response.ContentType = "text/html";await context.Response.WriteAsync("1 start<br/>"); // 1. 进入时执行await next.Invoke(); // 2. 调用下一个中间件await context.Response.WriteAsync("1 end<br/>"); // 7. 返回时执行
});pipeBuilder.Use(async (context, next) =>
{await context.Response.WriteAsync("2 start<br/>"); // 3. 进入时执行await next.Invoke(); // 4. 调用下一个中间件await context.Response.WriteAsync("2 end<br/>"); // 6. 返回时执行
});pipeBuilder.Use(async (context, next) =>
{await context.Response.WriteAsync("3 start<br/>"); // 5. 进入时执行await next.Invoke(); // 调用Run(没有下一个中间件)await context.Response.WriteAsync("3 end<br/>");
});
终端中间件
pipeBuilder.Run(async context =>
{await context.Response.WriteAsync("Run<br/>"); // 5.5 执行并终止管道
});
实际输出结果
访问 /test
路径时,浏览器会显示:
1 start
2 start
3 start
Run
3 end
2 end
1 end
关键概念说明
- Use() - 添加中间件,可以调用下一个中间件
- Run() - 终端中间件,终止管道传递
- next.Invoke() - 调用管道中的下一个中间件
- 洋葱模型 - 中间件的执行顺序:进入时正序,返回时逆序
这个示例很好地展示了ASP.NET Core中间件管道的执行原理和"洋葱模型"的工作方式。
-
注意事项: 此时若执行
Run()
以后,依然运行pipeBuilder.Use
是不会生效了- 通俗理解,相当于已经return了,那么return后面再写语句,肯定是不会执行的
......pipeBuilder.Run(async context =>{await context.Response.WriteAsync("Run<br/>");});// 不会执行pipeBuilder.Use(async (context, next) =>{await context.Response.WriteAsync("4 start<br/>");await next.Invoke();await context.Response.WriteAsync("4 end<br/>");});
简单的自定义中间件
- 如果中间件的代码比较复杂,或者我们需要重复使用一个中间件的话,我们最好把中间件的代码放到一个单独的“中间件类”中。
- 中间件类是一个普通的.NET类,它不需要继承任何父类或者实现任何接口,但是这个类需要有一个构造方法,构造方法至少要有 一个RequestDelegate类型的参数,这个参数用来指向下一个中间件。这个类还需要定义一个名字为Invoke或InvokeAsync的 方法,方法至少有一个HttpContext类型的参数,方法的返回值必须是Task类型。中间件类的构造方法和lnvoke(或 lnvokeAsync)方法还可以定义其他参数,其他参数的值会通过依赖注入自动赋值。
// Test1Middleware.csnamespace WebApplicationAboutMiddleWare
{public class Test1Middleware{private readonly RequestDelegate next;public Test1Middleware(RequestDelegate next){this.next = next;}public async Task InvokeAsync(HttpContext context){await context.Response.WriteAsync("I'm in Test1Middleware: start...<br/>");await next.Invoke(context);await context.Response.WriteAsync("I'm in Test1Middleware: end...<br/>");}}
}// Program.cs......
app.Map("/test", async (pipeBuilder) =>
{pipeBuilder.Use(async (context, next) =>{context.Response.ContentType = "text/html";await context.Response.WriteAsync("1 start<br/>");await next.Invoke();await context.Response.WriteAsync("1 end<br/>");});pipeBuilder.Use(async (context, next) =>{await context.Response.WriteAsync("2 start<br/>");await next.Invoke();await context.Response.WriteAsync("2 end<br/>");});pipeBuilder.Use(async (context, next) =>{await context.Response.WriteAsync("3 start<br/>");await next.Invoke();await context.Response.WriteAsync("3 end<br/>");});// 应用pipeBuilder.UseMiddleware<Test1Middleware>();pipeBuilder.Run(async context =>{await context.Response.WriteAsync("Run<br/>");});});app.Run();- 测试: https://localhost:7118/test,返回结果1 start
2 start
3 start
I'm in Test1Middleware: start...
Run
I'm in Test1Middleware: end...
3 end
2 end
1 end