Asp.net处理http请求和响应的管道---中间件

Asp.net处理http请求和响应的管道---中间件

中间件是一种处理HTTP请求和响应的组件,位于应用程序的请求处理管道中,可以在请求到达应用程序和生成相应之间执行一些逻辑。如身份验证、异常处理、记录日志、响应压缩、跨域处理等,asp.net core内置了很多中间件,同时也支持自定义中间件。

中间件工作方式

每个中间件可进行的工作如下:

  • 选择是否将请求向下传递
  • 设置在下一个中间件前面或者后面执行
  • 请求委托用户生成请求管道
  • 请求委托处理每个http请求
image-20241125160251420

编写中间件

最常用和最简单的就是使用app.Use的方式

1
2
3
4
5
6
app.Use(async (context,next) =>
{
//下游中间件执行前
await next.Invoke();//执行下游中间件
//下游中间件执行后
});

案例

1
2
3
4
5
6
app.Use(async (context,next) =>
{
//在请求头中增加一项目,管道下游就能读取到
context.Request.Headers.Add("测试", "Test");
await next.Invoke();
});
1
2
3
4
5
6
7
8
app.MapGet("/test", (context) =>
{
foreach (var item in context.Request.Headers)
{
Console.WriteLine($"{item.Key}:{item.Value}");
}
return Task.CompletedTask;
});

访问/test,就可以直接打印出以下内容

image-20241125162826065

除了使用app.Use方式,还有很多其他方式

  • Map():用作约定来创建管道分支
  • MapWhen():基于给定谓词的结果创建管道分支
  • Run():终止管道,不再接受到next参数
  • UseMiddleware():常用来封装中间件

UseMiddleware

基于约定

最常用的封装中间件的方式之一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UseMiddlewareDemo
{
private readonly RequestDelegate _next;
public UseMiddlewareDemo(RequestDelegate next)
{
_next = next;
}

public async Task InvokeAsync(HttpContext context)
{
context.Request.Headers.Add("中间件测试", "我是一个中间件类");
await _next(context);
}
}

直接使用app.UseMiddleware<UseMiddlewareDemo>();结果是:

image-20241126105512445

注意事项:编写中间件是基于约定编写,必须得约定:

  • 必须有公共构造函数,参数必须有RequestDelegate
  • 必须有名称为Invoke或者InvokeAsync的公共方法,且必须返回Task类型,同时第一个参数必须为HttpContext
  • 构造函数和Invoke/InvokeAsync的其他参数由依赖注入填充

生命周期注入问题

Middleware会自动注入一个单例,所以在构造函数进行注入时,不能注入Scope生命周期的服务。如果要注入Scope的生命周期服务,直接在Invoke/InvokeAsync中注入即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//简单声明一个服务
public interface ITest
{
public string Name { get; }
}

public class TestScopeDemo : ITest
{
public string Name => "Scope生命周期测试";

}
//在中间件中使用
public class UseMiddlewareDemo
{
private readonly RequestDelegate _next;
public UseMiddlewareDemo(RequestDelegate next)
{
_next = next;
}

public async Task InvokeAsync(HttpContext context,ITest test)
{
context.Request.Headers.Add("中间件测试", "我是一个中间件类");
context.Request.Headers.Add("中间件Scope", test.Name);
await _next(context);
}
}

//在program.cs中进行添加Scope生命周期的注册
builder.Services.AddScoped<ITest,TestScopeDemo>();

结果是:

image-20241126113303335

基于工厂IMiddleware

除了上述基于约定来实现中间件,还可以基于工厂的中间件激活扩展,即实现IMiddleware接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MiddlewareDemoFactory : IMiddleware
{

readonly ITest _test;
public MiddlewareDemoFactory(ITest test)
{
_test = test;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
context.Request.Headers.Add("中间件测试IMiddleware", "我是一个中间件类IMiddleware");
context.Request.Headers.Add("中间件Scope-IMiddleware", _test.Name);
await next(context);
}
}

另外,实现了IMiddleware的中间件需要在容器中进行注册,声明周期可以自定义

builder.Services.AddScoped<MiddlewareDemoFactory>();

使用中间件

app.UseMiddleware<MiddlewareDemoFactory>();

image-20241126115520254

两者区别

基于约定基于工厂(IMiddleware)
在构造函数中无法注入Scope声明周期的服务,只能通过Invoke方法注入只能通过构造函数注入,Invoke无法注入(因为接口的参数限定)
无需再容器中注册必须在容器中注册
生命周期为单例生命周期可自定义

中间件的执行顺序

中间件是一种管道,放置的顺序就是执行的顺序,所以一定要注意中间件的执行顺序。

image-20241126120241850

Asp.net处理http请求和响应的管道---中间件

https://bubuweiying.site/Aspnet处理http请求和响应的管道中间件/

作者

步步为营

发布于

2024-11-25

更新于

2025-03-15

许可协议