ASP.NET?Core的中間件與管道介紹
今天來討論一個(gè)ASP.NET Core 很重要概念管道和中間件,在ASP.NET Core中,針對HTTP請求采用pipeline也就是通常說的管道方式來處理,而管道容器內(nèi)可以掛載很多中間件(處理邏輯)“串聯(lián)”來處理HTTP請求,每一個(gè)中間件都有權(quán)決定是否需要執(zhí)行下一個(gè)中間件,或者直接做出響應(yīng)。這樣的機(jī)制使得HTTP請求能夠很好的被層層處理和控制,并且層次清晰處理起來甚是方便。 示意圖如下:

為了再次說明管道和中間件的概念,舉一個(gè)官方給出的權(quán)限驗(yàn)證的例子,中間件A,B分別按順序掛載在管道容器中,A為權(quán)限驗(yàn)證中間件,只有通過A的權(quán)限驗(yàn)證才能執(zhí)行B,如果沒有通過A的驗(yàn)證,A有權(quán)中斷管道處理直接返回相應(yīng)的錯(cuò)誤提示例如401等。這樣必須由上一節(jié)點(diǎn)來調(diào)用的串行遞歸執(zhí)行方式就是pipeline,而每一個(gè)節(jié)點(diǎn)就是中間件或者叫中間組件?,F(xiàn)在我們來看看如何在ASP.NET Core中使用中間件和管理自己的HTTP管道
環(huán)境配置與Startup
在了解中間件之前我們需要先知道Startup這個(gè)類具體運(yùn)作方式,我們以下面這段代碼為例:
/// <summary>
/// web宿主的入口類
/// </summary>
public class Startup
{
//加入服務(wù)項(xiàng)到容器, 這個(gè)方法將會(huì)被runtime調(diào)用
public void ConfigureServices(IServiceCollection services)
{
}
/// <summary>
/// 配置HTTP請求管道
/// </summary>
/// <param name="app">被用于構(gòu)建應(yīng)用程序的請求管道 只可以在Startup中的Configure方法里使用</param>
/// <param name="env">提供了訪問應(yīng)用程序?qū)傩?,如環(huán)境變量</param>
/// <param name="loggerFactory">提供了創(chuàng)建日志的機(jī)制</param>
public void Configure(IApplicationBuilder app,IHostingEnvironment env,ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment()) //根據(jù)配置的環(huán)境為開發(fā)環(huán)境,則會(huì)配置拋出異常錯(cuò)誤界面
{
app.UseDeveloperExceptionPage(); //拋出詳細(xì)的異常錯(cuò)誤界面
}
//管道斷路
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}可以看到 Startup.cs 內(nèi)有兩個(gè)方法,一個(gè)是用來配置接口服務(wù)到管道容器中的ConfigureServices, 一個(gè)是用來配置管道中間件的Configure。
為什么必須是這兩個(gè)方法名?
其實(shí)這兩個(gè)方法名并不是規(guī)定死的,但也不是任意規(guī)定的,他是根據(jù)容器的環(huán)境變量來判斷的,這里先給出官方文檔《多環(huán)境下工作》。
我們可以在文檔中了解到,Core使用“ASPNETCORE_ENVIRONMENT”字段來描述當(dāng)前運(yùn)行環(huán)境名稱這就是上文中提到的環(huán)境配置,官方預(yù)設(shè)了3個(gè)環(huán)境名分別是Development(開發(fā)環(huán)境), Staging(測試環(huán)境), Production(生產(chǎn)環(huán)境),如果您使用的是VSCode您可以在.vscode文件夾下的launch.json中找到“ASPNETCORE_ENVIRONMENT”字段,可以發(fā)現(xiàn)默認(rèn)情況下是Development,那說這些到底有什么用呢?

在Startup中規(guī)定,配置服務(wù)和中間件兩個(gè)方法可以根據(jù)環(huán)境名稱來命名和選擇調(diào)用,命名規(guī)則為ConfigureServices{ENVIRONMENT}和Configure{ENVIRONMENT}。如ASPNETCORE_ENVIRONMENT = “Development” 則ConfigureServices和Configure 可以寫成ConfigureServicesDevelopment 和ConfigureDevelopment ,其他也是如此。這樣就可以通過配置ASPNETCORE_ENVIRONMENT 來決定該調(diào)用哪一個(gè)配置方法了。
ConfigureServices和Configure是什么環(huán)境下的呢?
ConfigureServices和Configure就好像Switch 語句中的 default一樣的道理,如果沒有找到任何符合環(huán)境名的方法名,就會(huì)執(zhí)行調(diào)用這兩個(gè)方法。如配置了Development,但卻沒有給出ConfigureServicesDevelopment ,這時(shí)就會(huì)執(zhí)行ConfigureServices,如果都沒有就會(huì)拋出異常。
必須設(shè)置成預(yù)設(shè)環(huán)境名嗎?
環(huán)境名配置的參數(shù)名不必是預(yù)設(shè)值,你可以自己寫一個(gè),比如LogEnv等等。
接下來我們看一下實(shí)現(xiàn)的代碼:
/// <summary>
/// web宿主的入口類
/// </summary>
public class Startup
{
//加入服務(wù)項(xiàng)到容器, 這個(gè)方法將會(huì)被runtime調(diào)用
public void ConfigureServices(IServiceCollection services)
{
}
/// <summary>
/// Log環(huán)境下配置HTTP請求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureLogHelp(IApplicationBuilder app){
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World - ConfigureLogHelp");
});
}
/// <summary>
/// 開發(fā)環(huán)境下配置HTTP請求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World - ConfigureDevelopment");
});
}
/// <summary>
/// 默認(rèn)情況下配置HTTP請求管道
/// </summary>
/// <param name="app">被用于構(gòu)建應(yīng)用程序的請求管道。只可以在 Startup 中的 Configure 方法里使用</param>
/// <param name="env">提供了訪問應(yīng)用程序?qū)傩?,如環(huán)境變量</param>
/// <param name="loggerFactory">提供了創(chuàng)建日志的機(jī)制</param>
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//管道斷路
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}當(dāng)ASPNETCORE_ENVIRONMENT = “Development”

當(dāng)ASPNETCORE_ENVIRONMENT = “LogHelp”

這樣做的好處就是你可以寫自己的測試配置而不會(huì)影響到其他人或者開發(fā)過程。當(dāng)然環(huán)境的作用還在于前端應(yīng)該引用什么樣的CSS和JS,關(guān)于這些我們之后在MVC的章節(jié)再來討論, 想了解的博友可以看官方文檔
管道配置與Startup
說完環(huán)境配置和Startup的關(guān)系,我們回來接著聊管道的事情,現(xiàn)在我們來說說Configure{ENVIRONMENT}一下Configure簡稱這個(gè)方法。
Configure這個(gè)方法是用于配置中間件到中管道容器(IApplicationBuilder),所以這個(gè)方法必須要包含一個(gè)IApplicationBuilder參數(shù)用來接受管道容器,方便開發(fā)者配置。當(dāng)然他還可以接受其他的可選參數(shù)供開發(fā)者使用如下:
(注:下圖來源于ASP.NET Core中文文檔)

需要提一下的是,剛剛我們上文中說的環(huán)境名在IHostingEnvironment中可以獲取,對于預(yù)設(shè)值官方還做了判斷封裝,當(dāng)然你可以重構(gòu)它來封裝自己的環(huán)境名判斷。
HTTP管道容器由三個(gè)擴(kuò)展的方法來控制中間件的路由、掛載等等,分別是Run, Map, User。
a.Run方法會(huì)使得可以使管道短路,顧名思義就是終結(jié)管道向下執(zhí)行不會(huì)調(diào)用next()委托,所以Run方法最好放在管道的最后來執(zhí)行,如下面的代碼:
/// <summary>
/// 開發(fā)環(huán)境下配置HTTP請求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World - ConfigureDevelopment");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World - ConfigureDevelopment 不會(huì)被執(zhí)行");
});
}執(zhí)行結(jié)果:

b.Use不會(huì)主動(dòng)短路整個(gè)HTTP管道,但是也不會(huì)主動(dòng)調(diào)用下一個(gè)中間件,必須自行調(diào)用await next.Invoke(); 如果不使用這個(gè)方法去調(diào)用下一個(gè)中間件那么Use此時(shí)的效果其實(shí)和Run是相同的,我們來看正常的代碼:
/// <summary>
/// 開發(fā)環(huán)境下配置HTTP請求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
var order ="";
app.Use(async (context, next) =>
{
order = $"{order}|Use start";
await next.Invoke();
order = $"{order}|Use end";
});
app.Run(async context =>
{
await context.Response.WriteAsync($"{order}|Run ext");
});
}執(zhí)行結(jié)果如下:

可以看到,Use end并沒有被執(zhí)行到,因?yàn)樵谡{(diào)用下一個(gè)中間件時(shí)采用了Run,管道被終止了。
再來看看如果不顯式調(diào)用next.Invoke()時(shí)的代碼:
/// <summary>
/// 開發(fā)環(huán)境下配置HTTP請求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
var order ="";
app.Use(async (context, next) =>
{
order = $"{order}|Use start";
//去掉顯示調(diào)用下一個(gè)中間件
//await next.Invoke();
order = $"{order}|Use end";
await context.Response.WriteAsync(order);
});
app.Run(async context =>
{
await context.Response.WriteAsync($"{order}|Run ext");
});
}其結(jié)果如下:

可以發(fā)現(xiàn)Run這個(gè)中間件并沒有被執(zhí)行,而只是單純的執(zhí)行了Use這個(gè)中間件。所以說 在不顯式調(diào)用下一個(gè)中間件的情況下,效果和Run時(shí)一樣的會(huì)使管道短路。
c.Map可以根據(jù)提供的URL來路由中間件,如下代碼判斷URL中訪問"/test"時(shí)就會(huì)執(zhí)行某個(gè)中間件邏輯:
/// <summary>
/// 開發(fā)環(huán)境下配置HTTP請求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
app.Map("/test", HandleMapTest);
}
/// <summary>
/// maptest 處理方法
/// </summary>
public void HandleMapTest(IApplicationBuilder app){
app.Run(async (context) =>
{
await context.Response.WriteAsync("HandleMapTest Handler");
});
}結(jié)果如下:

如果訪問/test就會(huì)執(zhí)行相應(yīng)的中間件,反之則不會(huì)執(zhí)行。
MapWhen是Map的一個(gè)條件判斷的擴(kuò)展方法,可以通過它來判斷某個(gè)條件適合的時(shí)候執(zhí)行某一個(gè)中間件,如:當(dāng)攜帶某一個(gè)參數(shù)名稱時(shí),執(zhí)行某一個(gè)中間件或者反之,代碼如下:
/// <summary>
/// 開發(fā)環(huán)境下配置HTTP請求管道
/// </summary>
/// <param name="app"></param>
public void ConfigureDevelopment(IApplicationBuilder app){
app.MapWhen(context => {
return context.Request.Query.ContainsKey("username");
}, HandleUserName);
app.Run(async context =>
{
await context.Response.WriteAsync("default ext");
});
}
/// <summary>
///
/// </summary>
public void HandleUserName(IApplicationBuilder app){
app.Run(async context =>
{
await context.Response.WriteAsync("UserName Map");
});
}結(jié)果如下:


Map還可以進(jìn)行嵌套路由中間件,這里不再描述,大家可以參看這里。
到此這篇關(guān)于ASP.NET Core中間件與管道介紹的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ASP.NET中 RadioButtonList 單選按鈕組控件的使用方法
本文主要簡單介紹RadioButtonList控件的常見屬性和使用方法,希望對大家有所幫助。2016-04-04
.Net創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory)
這篇文章介紹了.Net設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory),文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
C#Web應(yīng)用程序入門經(jīng)典學(xué)習(xí)筆記之二
C#Web應(yīng)用程序入門經(jīng)典學(xué)習(xí)筆記之二...2006-08-08
ASP.NET中Literal與Label控件的區(qū)別
相信學(xué)ASP.NET的朋友們都會(huì)遇到Literal和Label到底該用那個(gè)的問題,Literal和Label到底有什么不同,其實(shí)簡單的講就是Literal不會(huì)產(chǎn)生HTML代碼,而Label會(huì)產(chǎn)生一個(gè)span標(biāo)記,下面為大家詳細(xì)講解一下。2016-04-04
在ASP.NET 中實(shí)現(xiàn)單點(diǎn)登錄
在ASP.NET 中實(shí)現(xiàn)單點(diǎn)登錄...2007-03-03

