前言
本篇文章是我們在開發自己的專案中實際使用的,比較貼合實際應用,算是對中間件的一個深入使用了,不是簡單的Hello World。
中間件(Middleware)的作用
我們知道,任何的一個web框架都是把http請求封裝成一個管道,每一次的請求都是經過管道的一系列操作,最終到達我們寫的程式碼中。那麼中間件就是在應用程式管道中的一個元件,用來攔截請求過程進行一些其他處理和回應。中間件可以有很多個,每個中間件都可以對管道中的請求進行攔截,它可以決定是否將請求轉移給下一個中間件。
asp.net core 提供了IApplicationBuilder介面來讓把中間件註冊到asp.net的管道請求當中去,中間件是一個典型的AOP應用。
每一個中間件都可以在請求前後進行操作。請求處理完成之後傳遞給下一個請求。
中間件的運作方式
預設情況下,中間件的執行順序根據Startup.cs檔案中,在public void Configure(IApplicationBuilder app){} 方法中註冊的先後順序執行。
大概有3種方式可以在管道中註冊"中間件"
1.app.Use(),IApplicationBuilder介面原生提供,註冊等都用它。
2.app.Run() ,是一個擴展方法,它需要一個RequestDelegate委託,裡麵包含了Http的上下文信息,沒有next參數,因為它總是在管道最後一步執行。
3.app.Map(),也是一個擴充方法,類似MVC的路由,用途一般是一些特殊請求路徑的處理。如:www.example.com/token 等。
上面的Run,Map內部也是呼叫的Use,算是對IApplicationBuilder介面擴充,如果你覺得名字都不夠準確,那麼下面這個擴充方法就是正宗的註冊中間件的了,也是功能最強大的。
app.UseMiddleware(),沒錯,就是這個了。 為什麼說功能強大呢?是因為它不但提供了註冊中間件的功能,還提供了依賴注入(DI)的功能,以後大部分情況就用它了。
中間件(Middleware)和過濾器(Filter)的區別
熟悉MVC框架的同學應該知道,MVC也提供了5大過濾器供我們用來處理請求前後需要執行的程式碼。分別是AuthenticationFilter,AuthorizationFilter,ActionFilter,ExceptionFilter,ResultFilter。
根據描述,可以看出中間件和過濾器的功能類似,那麼他們有什麼區別?為什麼又要搞一個中間件呢?
其實,過濾器和中間件他們的關注點是不一樣的,也就是說職責不一樣,乾的事情就不一樣。
舉個栗子,中間件像是埃辛諾斯戰刃,過濾器像是巨龍之怒,泰蕾苟薩的寄魂杖,你一個戰士拿著巨龍之怒,泰蕾苟薩的寄魂杖去戰場殺人,雖然都有傷害,但是你拿著法杖傷害低不說,還減屬性啊。
同作為兩個AOP利器,過濾器更貼合業務,它專注於應用程式本身,例如你看ActionFilter 和ResultFilter,它都直接和你的Action,ActionResult互動了,是不是離你很近的感覺,那我有一些例如對我的輸出結果進行格式化啦,對我的請求的ViewModel進行資料驗證啦,肯定就是用Filter無疑了。它是MVC的一部分,它可以攔截到你Action上下文的一些訊息,而中間件是沒有這個能力的。
什麼情況我們需要中間件
那麼,何時使用中間件呢?我的理解是在我們的應用程式當中和業務關係不大的一些需要在管道中做的事情可以使用,比如身份驗證,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(),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值,平台验证用户合法性的接口地址等参数。
建立一个Options类进行配置的设置,我们取名字为:PlatformAuthenticationOptions,继承AuthenticationOptions,并且实现掉IOptions8742468051c85b06f0a0af9e3e506b5c接口,这样子就能在Startup中直接配置了。
我们只需要重写AuthenticationMiddleware中的CreateHandler方法就行了,在Handler中可以实现掉我们中间件的功能。
然后创建一个处理的Handler类,取名为PlatformAuthenticationHandler,继承于AuthenticationHandler58fec2039d3938dc329cad313e09b70a用来处理请求中的调用。
至此,我们的核心需要的类已经建立完了,剩下的就是填充代码。
1.在PlatformAuthenticationHandler中重写HandleAuthenticateAsync()方法 , 进行主流程的控制。
2.在PlatformAuthenticationHandler中重写FinishResponseAsync()方法,进行Session的存储操作。
3.在PlatformAuthenticationHandler中重写HandleSignOutAsync()方法,进行登出的控制,因为用户登出之后我们要通知平台做一些其他操作。
4.在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(); }
现在,我们的中间件核心业务流程的实现已经出来了,我就不大篇幅的粘贴代码了,会影响阅读,感兴趣具体实现的朋友可以去下面的地址查看代码,有具体流程的注释。
以上就是本文的全部内容,希望对大家的学习有所帮助,更多浅谈ASP.NET Core 中间件详解及项目实战相关文章请关注PHP中文网!