这批.Net程序员水平不行啊!居然ASP.NET Core Middleware都不会用
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了这批.Net程序员水平不行啊!居然ASP.NET Core Middleware都不会用相关的知识,希望对你有一定的参考价值。
最近问了几个面试同一个问题:如果有多个自定义Middleware,如何控制它们的执行顺序(比如先判断用户合法再写访问日志)。居然大部分人答不上来?!
对此,你有什么看法?
ASP.NET Core Middleware的调用顺序
这里我直接贴上官方文档[1]中的图片:
ASP.NET Core 请求管道包含一系列请求委托,依次调用。而调用顺序实际上就是我们在Startup.cs中注册(使用UseMiddlewareExtensions.UseMiddleware方法
)它们的顺序。
示例代码如下:
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,而且会使代码比较凌乱,可读性较差。
可以使用下面的方式,简化注册代码:
public void ConfigureServices(IServiceCollection services)
{
services.AddMiddlewares();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddlewares();
}
实现方式
1.定义Attribute
MiddlewareRegisterAttribute
将放在每个Middleware实现类上,表明它是需要被注册的Middleware。
[AttributeUsage(AttributeTargets.Class)]
public class MiddlewareRegisterAttribute : Attribute
{
//注册顺序
public int Sort { get; set; } = int.MaxValue;
//生命周期
public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Scoped;
}
2.定义注册信息类
MiddlewareRegisterInfo
用于存放Middleware的注册信息,供注册方法调用。
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; }
}
2.实现注册扩展方法
读取Assembly中的Type, 如果存在MiddlewareRegisterAttribute
就把它放入List<MiddlewareRegisterInfo>
列表中,最后根据Sort
属性顺序依次注册,代码如下:
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,功能仅仅是输出日志:
[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,比较分散。但整体来说,代码更易读!
如果你有其他实现方式,欢迎到公众号后台留言指教。
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!
参考资料
[1]
官方文档: https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware
以上是关于这批.Net程序员水平不行啊!居然ASP.NET Core Middleware都不会用的主要内容,如果未能解决你的问题,请参考以下文章
能说一下ADO.NET 和.NET,还有asp.NET的区别吗?