淺談ASP.NET Core 中間件詳解及項(xiàng)目實(shí)戰(zhàn)
前言
本篇文章是我們在開發(fā)自己的項(xiàng)目中實(shí)際使用的,比較貼合實(shí)際應(yīng)用,算是對中間件的一個(gè)深入使用了,不是簡單的Hello World。
中間件(Middleware)的作用
我們知道,任何的一個(gè)web框架都是把http請求封裝成一個(gè)管道,每一次的請求都是經(jīng)過管道的一系列操作,最終到達(dá)我們寫的代碼中。那么中間件就是在應(yīng)用程序管道中的一個(gè)組件,用來攔截請求過程進(jìn)行一些其他處理和響應(yīng)。中間件可以有很多個(gè),每一個(gè)中間件都可以對管道中的請求進(jìn)行攔截,它可以決定是否將請求轉(zhuǎn)移給下一個(gè)中間件。
asp.net core 提供了IApplicationBuilder接口來讓把中間件注冊到asp.net的管道請求當(dāng)中去,中間件是一個(gè)典型的AOP應(yīng)用。
每一個(gè)中間件都都可以在請求之前和之后進(jìn)行操作。請求處理完成之后傳遞給下一個(gè)請求。
中間件的運(yùn)行方式
默認(rèn)情況下,中間件的執(zhí)行順序根據(jù)Startup.cs文件中,在public void Configure(IApplicationBuilder app){} 方法中注冊的先后順序執(zhí)行。
大概有3種方式可以在管道中注冊"中間件"
1.app.Use(),IApplicationBuilder接口原生提供,注冊等都用它。
2.app.Run() ,是一個(gè)擴(kuò)展方法,它需要一個(gè)RequestDelegate委托,里面包含了Http的上下文信息,沒有next參數(shù),因?yàn)樗偸窃诠艿雷詈笠徊綀?zhí)行。
3.app.Map(),也是一個(gè)擴(kuò)展方法,類似于MVC的路由,用途一般是一些特殊請求路徑的處理。如:www.example.com/token 等。
上面的Run,Map內(nèi)部也是調(diào)用的Use,算是對IApplicationBuilder接口擴(kuò)充,如果你覺得名字都不夠準(zhǔn)確,那么下面這個(gè)擴(kuò)展方法就是正宗的注冊中間件的了,也是功能最強(qiáng)大的。
app.UseMiddleware<>(),沒錯(cuò),就是這個(gè)了。 為什么說功能強(qiáng)大呢?是因?yàn)樗坏峁┝俗灾虚g件的功能,還提供了依賴注入(DI)的功能,以后大部分情況就用它了。
中間件(Middleware)和過濾器(Filter)的區(qū)別
熟悉MVC框架的同學(xué)應(yīng)該知道,MVC也提供了5大過濾器供我們用來處理請求前后需要執(zhí)行的代碼。分別是AuthenticationFilter,AuthorizationFilter,ActionFilter,ExceptionFilter,ResultFilter。
根據(jù)描述,可以看出中間件和過濾器的功能類似,那么他們有什么區(qū)別?為什么又要搞一個(gè)中間件呢?
其實(shí),過濾器和中間件他們的關(guān)注點(diǎn)是不一樣的,也就是說職責(zé)不一樣,干的事情就不一樣。
舉個(gè)栗子,中間件像是埃辛諾斯戰(zhàn)刃,過濾器像是巨龍之怒,泰蕾茍薩的寄魂杖 ,你一個(gè)戰(zhàn)士拿著巨龍之怒,泰蕾茍薩的寄魂杖去戰(zhàn)場殺人,雖然都有傷害,但是你拿著法杖傷害低不說,還減屬性啊。
同作為兩個(gè)AOP利器,過濾器更貼合業(yè)務(wù),它關(guān)注于應(yīng)用程序本身,比如你看ActionFilter 和 ResultFilter,它都直接和你的Action,ActionResult交互了,是不是離你很近的感覺,那我有一些比如對我的輸出結(jié)果進(jìn)行格式化啦,對我的請求的ViewModel進(jìn)行數(shù)據(jù)驗(yàn)證啦,肯定就是用Filter無疑了。它是MVC的一部分,它可以攔截到你Action上下文的一些信息,而中間件是沒有這個(gè)能力的。
什么情況我們需要中間件
那么,何時(shí)使用中間件呢?我的理解是在我們的應(yīng)用程序當(dāng)中和業(yè)務(wù)關(guān)系不大的一些需要在管道中做的事情可以使用,比如身份驗(yàn)證,Session存儲(chǔ),日志記錄等。其實(shí)我們的 asp.net core項(xiàng)目中本身已經(jīng)包含了很多個(gè)中間件。
舉例,我們在新建一個(gè) asp.net core應(yīng)用程序的時(shí)候,默認(rèn)生成的模板當(dāng)中
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去查看源碼:
//擴(kuò)展方法`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>());
}
}
//擴(kuò)展方法`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()等等都是通過中間件實(shí)現(xiàn)的。
怎么樣自定義自己的中間件
背景:我們項(xiàng)目使用到中間件的情景是,需要和其他部門進(jìn)行用戶(User)信息的共享。 以平臺(tái)和子系統(tǒng)舉例,我們正在開發(fā)一個(gè)子系統(tǒng),其中用戶信息,登錄,注冊等功能是放在平臺(tái)上的,這是一個(gè)跨多語言的系統(tǒng),平臺(tái)是Java語言開發(fā),用戶在訪問子系統(tǒng)的一些頁面的時(shí)候需要驗(yàn)證是否登錄,另外一些頁面是不需要驗(yàn)證是否登錄的,所以需要一個(gè)身份驗(yàn)證系統(tǒng)來代替Identity的功能。
幸運(yùn)的是微軟已經(jīng)給我們提供了一套身份驗(yàn)證的中間件,在Microsoft.AspNetCore.Authentication命名空間下,我們只需要拓展,添加自己的功能就行了 。具體怎么做呢?直接看代碼吧。
根據(jù)約定俗成,中間件類需要有一個(gè)Invoke方法,簽名是public async Task Invoke(HttpContext context){},下面是一個(gè)中間件的示例類:
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。
我們需要一個(gè)流程圖來理清邏輯思路,以便于寫代碼的時(shí)候思路更加的清晰。

平臺(tái)有一個(gè)要求就是,用戶在我們子系統(tǒng)退出之后,要調(diào)用平臺(tái)的一個(gè)接口通知他們,他們要做一些后續(xù)的業(yè)務(wù)。
OK,開始擼碼。
- 首先創(chuàng)建一個(gè)
PlatformAuthoricationMiddleware,它繼承于Microsoft.AspNetCore.Authentication下的類AuthenticationMiddleware,由于AuthenticationMiddleware已經(jīng)實(shí)現(xiàn)了Invoke功能,所以我們只需要重寫(override)它里面的一些方法就可以了。等等,我們好像還需要一些配置,比如流程圖中的ReturnUrl,平臺(tái)的Cookie的Key值,平臺(tái)驗(yàn)證用戶合法性的接口地址等參數(shù)。 - 建立一個(gè)
Options類進(jìn)行配置的設(shè)置,我們?nèi)∶譃椋?code>PlatformAuthenticationOptions,繼承AuthenticationOptions,并且實(shí)現(xiàn)掉IOptions<T>接口,這樣子就能在Startup中直接配置了。 - 我們只需要重寫
AuthenticationMiddleware中的CreateHandler方法就行了,在Handler中可以實(shí)現(xiàn)掉我們中間件的功能。 - 然后創(chuàng)建一個(gè)處理的Handler類,取名為
PlatformAuthenticationHandler,繼承于AuthenticationHandler<TOptions>用來處理請求中的調(diào)用。
至此,我們的核心需要的類已經(jīng)建立完了,剩下的就是填充代碼。
1.在PlatformAuthenticationHandler中重寫HandleAuthenticateAsync()方法 , 進(jìn)行主流程的控制。
2.在PlatformAuthenticationHandler中重寫FinishResponseAsync()方法,進(jìn)行Session的存儲(chǔ)操作。
3.在PlatformAuthenticationHandler中重寫HandleSignOutAsync()方法,進(jìn)行登出的控制,因?yàn)橛脩舻浅鲋笪覀円ㄖ脚_(tái)做一些其他操作。
4.在PlatformAuthenticationHandler中重寫HandleUnauthorizedAsync()方法,進(jìn)行未認(rèn)證操作。
最后,我們需要一個(gè)擴(kuò)展類來把我們的中間件以擴(kuò)展方法注冊到管道當(dāng)中去 。
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();
}
現(xiàn)在,我們的中間件核心業(yè)務(wù)流程的實(shí)現(xiàn)已經(jīng)出來了,我就不大篇幅的粘貼代碼了,會(huì)影響閱讀,感興趣具體實(shí)現(xiàn)的朋友可以去下面的地址查看代碼,有具體流程的注釋。
示例源碼:demo
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
用 Asp.Net 建立一個(gè)在線 RSS 新聞聚合器的方法
用 Asp.Net 建立一個(gè)在線 RSS 新聞聚合器的方法...2007-04-04
asp.net通過js實(shí)現(xiàn)Cookie創(chuàng)建以及清除Cookie數(shù)組的代碼
asp.net Cookie創(chuàng)建以及清除Cookie數(shù)組2010-03-03
如何使用簽名保證ASP.NET MVC OR WEBAPI的接口安全
這篇文章主要介紹了如何使用簽名保證ASP.NET MVC OR WEBAPI的接口安全,幫助大家更好的理解和學(xué)習(xí)使用.net技術(shù),感興趣的朋友可以了解下2021-04-04
asp.net下用Aspose.Words for .NET動(dòng)態(tài)生成word文檔中的圖片或水印的方法
本文詳細(xì)講解如何使用Aspose.Words for .NET的組件來生成word文檔與水印的方法,請看本文內(nèi)容。2010-04-04
ASP.NET Core中使用xUnit進(jìn)行單元測試
這篇文章主要介紹了ASP.NET Core中使用xUnit進(jìn)行單元測試,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
asp.net實(shí)現(xiàn)從Txt文件讀取數(shù)據(jù)到數(shù)據(jù)視圖的方法
這篇文章主要介紹了asp.net實(shí)現(xiàn)從Txt文件讀取數(shù)據(jù)到數(shù)據(jù)視圖的方法,涉及asp.net針對文本文件的遍歷操作與DataView的寫入操作相關(guān)技巧,需要的朋友可以參考下2015-12-12
.NET?實(shí)現(xiàn)啟動(dòng)時(shí)重定向程序運(yùn)行路徑及?Windows?服務(wù)運(yùn)行模式部署的方法
這篇文章主要介紹了.NET?實(shí)現(xiàn)啟動(dòng)時(shí)重定向程序運(yùn)行路徑及?Windows?服務(wù)運(yùn)行模式部署,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
Asp.net MVC利用knockoutjs實(shí)現(xiàn)登陸并記錄用戶的內(nèi)外網(wǎng)IP及所在城市(推薦)
這篇文章主要介紹了 Asp.net MVC利用knockoutjs實(shí)現(xiàn)登陸并記錄用戶的內(nèi)外網(wǎng)IP及所在城市(推薦),需要的朋友可以參考下2017-02-02
ASP.NET Gridview 中使用checkbox刪除的2種方法實(shí)例分享
ASP.NET Gridview 中使用checkbox刪除的2種方法實(shí)例分享,需要的朋友可以參考一下2013-06-06

