ASP.NET Core Middleware
的调用顺序

ASP.NET Core
请求管道包含一系列请求委托,依次调用。而调用顺序实际上就是我们在 Startup.cs
中注册(使用 UseMiddlewareExtensions.UseMiddleware
方法)它们的顺序。
示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public void ConfigureServices(IServiceCollection services)
{
//注册生命周期
services.AddTransient<TransientMiddleware>();
services.AddScoped<ScopedMiddleware>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//注册调用顺序
app.UseMiddleware<TransientMiddleware>();
app.UseMiddleware<ScopedMiddleware>();
}
|
但这种方式,对于调整自定义 Middleware
的需求,需要经常修改 Startup.cs
,而且会使代码比较凌乱,可读性较差。
可以使用下面的方式,简化注册代码:
1
2
3
4
5
6
7
8
|
public void ConfigureServices(IServiceCollection services)
{
services.AddMiddlewares();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddlewares();
}
|
实现方式
1. 定义 Attribute
MiddlewareRegisterAttribute
将放在每个 Middleware
实现类上,表明它是需要被注册的 Middleware
。
1
2
3
4
5
6
7
8
9
|
[AttributeUsage(AttributeTargets.Class)]
public class MiddlewareRegisterAttribute : Attribute
{
// 注册顺序
public int Sort { get; set; } = int.MaxValue;
// 生命周期
public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Scoped;
}
|
2. 定义注册信息类
MiddlewareRegisterInfo
用于存放 Middleware
的注册信息,供注册方法调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class MiddlewareRegisterInfo
{
public MiddlewareRegisterInfo(Type type,MiddlewareRegisterAttribute attribute)
{
Type = type;
Sort = attribute.Sort;
Lifetime = attribute.Lifetime;
}
public Type Type { get; private set; }
public int Sort { get; private set; }
public ServiceLifetime Lifetime { get; private set; }
}
|
3. 实现注册扩展方法
读取 Assembly
中的 Type
, 如果存在 MiddlewareRegisterAttribute
就把它放入 List<MiddlewareRegisterInfo>
列表中,最后根据 Sort
属性顺序依次注册,代码如下:
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
public static class MiddlewareRegisterExtensions
{
private static readonly IEnumerable<MiddlewareRegisterInfo> _middlewareRegisterInfos = GetMiddlewareRegisterInfos();
public static IServiceCollection AddMiddlewares(this IServiceCollection services)
{
foreach (var middlewareRegisterInfo in _middlewareRegisterInfos)
{
switch (middlewareRegisterInfo.Lifetime)
{
case ServiceLifetime.Singleton:
services.AddSingleton(middlewareRegisterInfo.Type);
break;
case ServiceLifetime.Transient:
services.AddTransient(middlewareRegisterInfo.Type);
break;
default:
services.AddScoped(middlewareRegisterInfo.Type);
break;
}
}
return services;
}
public static IApplicationBuilder UseMiddlewares(this IApplicationBuilder applicationBuilder)
{
foreach (var middlewareRegisterInfo in _middlewareRegisterInfos)
{
applicationBuilder.UseMiddleware(middlewareRegisterInfo.Type);
}
return applicationBuilder;
}
private static List<MiddlewareRegisterInfo> GetMiddlewareRegisterInfos()
{
var middlewareRegisterInfos = new List<MiddlewareRegisterInfo>();
//所有包含Middleware的Assembly
var assemblies = new Assembly[] { typeof(Startup).Assembly };
foreach (var assembly in assemblies)
{
foreach (var type in assembly.GetTypes().Where(x => !x.IsAbstract))
{
var attribute = type.GetCustomAttribute<MiddlewareRegisterAttribute>();
if (attribute != null)
{
middlewareRegisterInfos.Add(new MiddlewareRegisterInfo(type, attribute));
}
}
}
return middlewareRegisterInfos.OrderBy(p=>p.Sort).ToList();
}
}
|
测试一下
创建 3 个 Middleware
,功能仅仅是输出日志:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[MiddlewareRegister(Sort = 100)]
public class OneMiddleware : IMiddleware
{
private readonly ILogger<OneMiddleware> logger;
public OneMiddleware(ILogger<OneMiddleware> logger)
{
this.logger = logger;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
logger.LogInformation("One");
await next(context);
}
}
|
Sort
用 100、200、300,方便后面修改排序。
运行效果如下图:

现在,将 ThreeMiddleware 的 Sort 改为 150,调整注册顺序,再次运行,效果如下图:

结论
当然,这个解决方案也存在一些缺点,比如修改排序的位置移到每个 Middleware
,比较分散。但整体来说,代码更易读!