这批.Net 程序员水平不行啊!居然 ASP.NET Core Middleware 都不会用

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,比较分散。但整体来说,代码更易读!

0%