这批.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的Assemblyvar?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都不会用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MVP on Board 没用小技巧
- 下一篇: 使用 Blazor 开发内部后台(三):