在上篇文章主要介紹了DotNetCore項目情況,本篇文章是咱們在開發本身的項目中實際使用的,比較貼合實際應用,算是對中間件的一個深刻使用了,不是簡單的Hello World,若是你以爲本篇文章對你有用的話,不妨點個【推薦】。html
咱們知道,任何的一個web框架都是把http請求封裝成一個管道,每一次的請求都是通過管道的一系列操做,最終到達咱們寫的代碼中。那麼中間件就是在應用程序管道中的一個組件,用來攔截請求過程進行一些其餘處理和響應。中間件能夠有不少個,每個中間件均可以對管道中的請求進行攔截,它能夠決定是否將請求轉移給下一個中間件。git
asp.net core 提供了IApplicationBuilder
接口來讓把中間件註冊到asp.net的管道請求當中去,中間件是一個典型的AOP應用。 下面是一個微軟官方的一箇中間件管道請求圖:github
能夠看到,每個中間件都均可以在請求以前和以後進行操做。請求處理完成以後傳遞給下一個請求。web
默認狀況下,中間件的執行順序根據Startup.cs
文件中,在public void Configure(IApplicationBuilder app){}
方法中註冊的前後順序執行。
大概有3種方式能夠在管道中註冊"中間件"app
app.Use()
,IApplicationBuilder
接口原生提供,註冊等都用它。app.Run()
,是一個擴展方法,它須要一個RequestDelegate
委託,裏面包含了Http的上下文信息,沒有next參數,由於它老是在管道最後一步執行。app.Map()
,也是一個擴展方法,相似於MVC的路由,用途通常是一些特殊請求路徑的處理。如:www.example.com/token 等。上面的Run,Map內部也是調用的Use,算是對IApplicationBuilder接口擴充,若是你以爲名字都不夠準確,那麼下面這個擴展方法就是正宗的註冊中間件的了,也是功能最強大的。
app.UseMiddleware<>()
,沒錯,就是這個了。 爲何說功能強大呢?是由於它不但提供了註冊中間件的功能,還提供了依賴注入(DI)的功能,之後大部分狀況就用它了。框架
熟悉MVC框架的同窗應該知道,MVC也提供了5大過濾器供咱們用來處理請求先後須要執行的代碼。分別是AuthenticationFilter
,AuthorizationFilter
,ActionFilter
,ExceptionFilter
,ResultFilter
。asp.net
根據描述,能夠看出中間件和過濾器的功能相似,那麼他們有什麼區別?爲何又要搞一箇中間件呢?
其實,過濾器和中間件他們的關注點是不同的,也就是說職責不同,乾的事情就不同。async
舉個栗子,中間件像是
埃辛諾斯戰刃
,過濾器像是巨龍之怒,泰蕾苟薩的寄魂杖
,你一個戰士拿着巨龍之怒,泰蕾苟薩的寄魂杖
去戰場殺人,雖然都有傷害,可是你拿着法杖傷害低不說,還減屬性啊。ide
同做爲兩個AOP利器,過濾器更貼合業務,它關注於應用程序自己,好比你看ActionFilter
和 ResultFilter
,它都直接和你的Action,ActionResult交互了,是否是離你很近的感受,那我有一些好比對個人輸出結果進行格式化啦,對個人請求的ViewModel進行數據驗證啦,確定就是用Filter無疑了。它是MVC的一部分,它能夠攔截到你Action上下文的一些信息,而中間件是沒有這個能力的。ui
那麼,什麼時候使用中間件呢?個人理解是在咱們的應用程序當中和業務關係不大的一些須要在管道中作的事情可使用,好比身份驗證,Session存儲,日誌記錄等。其實咱們的 asp.net core項目中自己已經包含了不少箇中間件。
舉例,咱們在新建一個 asp.net core應用程序的時候,默認生成的模板當中
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { app.UseDeveloperExceptionPage(); app.UseStaticFiles(); loggerFactory.AddConsole(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
懶得去下載源碼了,咱們使用Reflector去查看源碼:
//擴展方法`app.UseDeveloperExceptionPage();` public static class DeveloperExceptionPageExtensions { // Methods public static IApplicationBuilder UseDeveloperExceptionPage(this IApplicationBuilder app) { if (app == null) { throw new ArgumentNullException("app"); } return UseMiddlewareExtensions.UseMiddleware<DeveloperExceptionPageMiddleware>(app, Array.Empty<object>()); } }
//擴展方法`app.UseStaticFiles();` public static class StaticFileExtensions { // Methods public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app) { if (app == null) { throw new ArgumentNullException("app"); } return UseMiddlewareExtensions.UseMiddleware<StaticFileMiddleware>(app, Array.Empty<object>()); } }
能夠看到 app.UseDeveloperExceptionPage()
,app.UseStaticFiles()
等等都是經過中間件實現的。
背景:咱們項目使用到中間件的情景是,須要和其餘部門進行用戶(User)信息的共享。 以平臺和子系統舉例,咱們正在開發一個子系統,其中用戶信息,登陸,註冊等功能是放在平臺上的,這是一個跨多語言的系統,平臺是Java語言開發,用戶在訪問子系統的一些頁面的時候須要驗證是否登陸,另一些頁面是不須要驗證是否登陸的,因此須要一個身份驗證系統來代替Identity的功能。
幸運的是微軟已經給咱們提供了一套身份驗證的中間件,在Microsoft.AspNetCore.Authentication
命名空間下,咱們只須要拓展,添加本身的功能就好了 。具體怎麼作呢?直接看代碼吧。
根據約定俗成,中間件類須要有一個Invoke方法,簽名是public async Task Invoke(HttpContext context){}
,下面是一箇中間件的示例類:
public class RequestLoggerMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public RequestLoggerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) { _next = next; _logger = loggerFactory.CreateLogger<RequestLoggerMiddleware>(); } public async Task Invoke(HttpContext context) { _logger.LogInformation("Handling request: " + context.Request.Path); await _next.Invoke(context); _logger.LogInformation("Finished handling request."); } }
瞭解了上面的約定以後,咱們就開始定義咱們本身的中間件Class。
咱們須要一個流程圖來理清邏輯思路,以便於寫代碼的時候思路更加的清晰。
平臺有一個要求就是,用戶在咱們子系統退出以後,要調用平臺的一個接口通知他們,他們要作一些後續的業務。
OK,開始擼碼。
PlatformAuthoricationMiddleware
,它繼承於Microsoft.AspNetCore.Authentication
下的類AuthenticationMiddleware
,因爲AuthenticationMiddleware
已經實現了Invoke功能,因此咱們只須要重寫(override)它裏面的一些方法就能夠了。等等,咱們好像還須要一些配置,好比流程圖中的ReturnUrl,平臺的Cookie的Key值,平臺驗證用戶合法性的接口地址等參數。PlatformAuthenticationOptions
,繼承AuthenticationOptions
,而且實現掉IOptions<T>
接口,這樣子就能在Startup中直接配置了。AuthenticationMiddleware
中的CreateHandler
方法就好了,在Handler中能夠實現掉咱們中間件的功能。PlatformAuthenticationHandler
,繼承於AuthenticationHandler<TOptions>
用來處理請求中的調用。至此,咱們的核心須要的類已經創建完了,剩下的就是填充代碼。
PlatformAuthenticationHandler
中重寫HandleAuthenticateAsync()
方法 , 進行主流程的控制。PlatformAuthenticationHandler
中重寫FinishResponseAsync()
方法,進行Session的存儲操做。PlatformAuthenticationHandler
中重寫HandleSignOutAsync()
方法,進行登出的控制,由於用戶登出以後咱們要通知平臺作一些其餘操做。PlatformAuthenticationHandler
中重寫HandleUnauthorizedAsync()
方法,進行未認證操做。最後,咱們須要一個擴展類來把咱們的中間件以擴展方法註冊到管道當中去 。
public static class MiddlewareExtensions { public static IApplicationBuilder UsePlatformAuthentication(this IApplicationBuilder app) { if (app == null) { throw new ArgumentNullException(nameof(app)); } return app.UseMiddleware<PlatformAuthenticationMiddleware>(); } public static IApplicationBuilder UsePlatformAuthentication(this IApplicationBuilder app, CookieAuthenticationOptions options) { if (app == null) { throw new ArgumentNullException(nameof(app)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } return app.UseMiddleware<PlatformAuthenticationMiddleware>(Options.Create(options)); } }
在Startup
中就是app.UsePlatformAuthentication()
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); //註冊PlatformAuthentication中間件 app.UsePlatformAuthentication(new PlatformAuthenticationOptions() { UserSessionStore = new UserSessionStore(), }); app.UseMvc(); }
如今,咱們的中間件核心業務流程的實現已經出來了,我就不大篇幅的粘貼代碼了,會影響閱讀,感興趣具體實現的朋友能夠去下面的地址查看代碼,有具體流程的註釋。
示例源碼:
https://github.com/yuleyule66/PlatformAuthMiddleware
本文地址:http://www.cnblogs.com/savorboard/p/5586229.html
做者博客:Savorboard 歡迎轉載,請保留出處