中間件是一種裝配到應(yīng)用管道以處理請(qǐng)求和響應(yīng)的軟件。 每個(gè)組件:
請(qǐng)求委托用于生成請(qǐng)求管道。 請(qǐng)求委托處理每個(gè) HTTP 請(qǐng)求。
使用 RunMap 和 Use 擴(kuò)展方法來(lái)配置請(qǐng)求委托。 可將一個(gè)單獨(dú)的請(qǐng)求委托并行指定為匿名方法(稱(chēng)為并行中間件),或在可重用的類(lèi)中對(duì)其進(jìn)行定義。 這些可重用的類(lèi)和并行匿名方法即為中間件,也叫中間件組件。 請(qǐng)求管道中的每個(gè)中間件組件負(fù)責(zé)調(diào)用管道中的下一個(gè)組件,或使管道短路。 當(dāng)中間件短路時(shí),它被稱(chēng)為“終端中間件”,因?yàn)樗柚怪虚g件進(jìn)一步處理請(qǐng)求。
將 HTTP 處理程序和模塊遷移到 ASP.NET Core 中間件 介紹了 ASP.NET Core 和 ASP.NET 4.x 中請(qǐng)求管道之間的差異,并提供了更多的中間件示例。
ASP.NET Core 請(qǐng)求管道包含一系列請(qǐng)求委托,依次調(diào)用。 下圖演示了這一概念。 沿黑色箭頭執(zhí)行。
每個(gè)委托均可在下一個(gè)委托前后執(zhí)行操作。 應(yīng)盡早在管道中調(diào)用異常處理委托,這樣它們就能捕獲在管道的后期階段發(fā)生的異常。
盡可能簡(jiǎn)單的 ASP.NET Core 應(yīng)用設(shè)置了處理所有請(qǐng)求的單個(gè)請(qǐng)求委托。 這種情況不包括實(shí)際請(qǐng)求管道。 調(diào)用單個(gè)匿名函數(shù)以響應(yīng)每個(gè) HTTP 請(qǐng)求。
C#
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});
}
}
第一個(gè) Run 委托終止了管道。
用 Use 將多個(gè)請(qǐng)求委托鏈接在一起。 next 參數(shù)表示管道中的下一個(gè)委托。 可通過(guò)不調(diào)用 next 參數(shù)使管道短路。 通??稍谙乱粋€(gè)委托前后執(zhí)行操作,如以下示例所示:
C#
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do work that doesn't write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
}
}
當(dāng)委托不將請(qǐng)求傳遞給下一個(gè)委托時(shí),它被稱(chēng)為“讓請(qǐng)求管道短路”。 通常需要短路,因?yàn)檫@樣可以避免不必要的工作。 例如,靜態(tài)文件中間件可以處理對(duì)靜態(tài)文件的請(qǐng)求,并讓管道的其余部分短路,從而起到終端中間件的作用。 如果中間件添加到管道中,且位于終止進(jìn)一步處理的中間件前,它們?nèi)蕴幚?nbsp;next.Invoke 語(yǔ)句后面的代碼。 不過(guò),請(qǐng)參閱下面有關(guān)嘗試對(duì)已發(fā)送的響應(yīng)執(zhí)行寫(xiě)入操作的警告。
警告
在向客戶(hù)端發(fā)送響應(yīng)后,請(qǐng)勿調(diào)用 next.Invoke。 響應(yīng)啟動(dòng)后,針對(duì) HttpResponse 的更改將引發(fā)異常。 例如,設(shè)置標(biāo)頭和狀態(tài)代碼更改將引發(fā)異常。 調(diào)用 next 后寫(xiě)入響應(yīng)正文:
HasStarted 是一個(gè)有用的提示,指示是否已發(fā)送標(biāo)頭或已寫(xiě)入正文。
向 Startup.Configure 方法添加中間件組件的順序定義了針對(duì)請(qǐng)求調(diào)用這些組件的順序,以及響應(yīng)的相反順序。 此排序?qū)τ诎踩?、性能和功能至關(guān)重要。
以下 Startup.Configure 方法將為常見(jiàn)應(yīng)用方案添加中間件組件:
C#
public void Configure(IApplicationBuilder app)
{
if (env.IsDevelopment())
{
// When the app runs in the Development environment:
// Use the Developer Exception Page to report app runtime errors.
// Use the Database Error Page to report database runtime errors.
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
// When the app doesn't run in the Development environment:
// Enable the Exception Handler Middleware to catch exceptions
// thrown in the following middlewares.
// Use the HTTP Strict Transport Security Protocol (HSTS)
// Middleware.
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// Use HTTPS Redirection Middleware to redirect HTTP requests to HTTPS.
app.UseHttpsRedirection();
// Return static files and end the pipeline.
app.UseStaticFiles();
// Use Cookie Policy Middleware to conform to EU General Data
// Protection Regulation (GDPR) regulations.
app.UseCookiePolicy();
// Authenticate before the user accesses secure resources.
app.UseAuthentication();
// If the app uses session state, call Session Middleware after Cookie
// Policy Middleware and before MVC Middleware.
app.UseSession();
// Add MVC to the request pipeline.
app.UseMvc();
}
在前面的示例代碼中,每個(gè)中間件擴(kuò)展方法都通過(guò) Microsoft.AspNetCore.Builder 命名空間在 IApplicationBuilder 上公開(kāi)。
UseExceptionHandler 是添加到管道的第一個(gè)中間件組件。 因此,異常處理程序中間件可捕獲稍后調(diào)用中發(fā)生的任何異常。
盡早在管道中調(diào)用靜態(tài)文件中間件,以便它可以處理請(qǐng)求并使其短路,而無(wú)需通過(guò)剩余組件。 靜態(tài)文件中間件不提供授權(quán)檢查。 可公開(kāi)訪問(wèn)由靜態(tài)文件中間件服務(wù)的任何文件,包括 wwwroot 下的文件。 若要了解如何保護(hù)靜態(tài)文件,請(qǐng)參閱 ASP.NET Core 中的靜態(tài)文件。
如果靜態(tài)文件中間件未處理請(qǐng)求,則請(qǐng)求將被傳遞給執(zhí)行身份驗(yàn)證的身份驗(yàn)證中間件 (UseAuthentication)。 身份驗(yàn)證不使未經(jīng)身份驗(yàn)證的請(qǐng)求短路。 雖然身份驗(yàn)證中間件對(duì)請(qǐng)求進(jìn)行身份驗(yàn)證,但僅在 MVC 選擇特定 Razor 頁(yè)或 MVC 控制器和操作后,才發(fā)生授權(quán)(和拒絕)。
以下示例演示中間件排序,其中靜態(tài)文件的請(qǐng)求在響應(yīng)壓縮中間件前由靜態(tài)文件中間件進(jìn)行處理。使用此中間件順序不壓縮靜態(tài)文件。 可以壓縮來(lái)自 UseMvcWithDefaultRoute 的 MVC 響應(yīng)。
C#
public void Configure(IApplicationBuilder app)
{
// Static files not compressed by Static File Middleware.
app.UseStaticFiles();
app.UseResponseCompression();
app.UseMvcWithDefaultRoute();
}
使用 Use、Run 和 Map 配置 HTTP 管道。 Use 方法可使管道短路(即不調(diào)用 next 請(qǐng)求委托)。 Run 是一種約定,并且某些中間件組件可公開(kāi)在管道末尾運(yùn)行的 Run[Middleware] 方法。
Map 擴(kuò)展用作約定來(lái)創(chuàng)建管道分支。 Map* 基于給定請(qǐng)求路徑的匹配項(xiàng)來(lái)創(chuàng)建請(qǐng)求管道分支。 如果請(qǐng)求路徑以給定路徑開(kāi)頭,則執(zhí)行分支。
C#
public class Startup
{
private static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
private static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
}
}
下表使用前面的代碼顯示來(lái)自 http://localhost:1234 的請(qǐng)求和響應(yīng)。
請(qǐng)求 | 響應(yīng) |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
使用 Map 時(shí),將從 HttpRequest.Path 中刪除匹配的線段,并針對(duì)每個(gè)請(qǐng)求將該線段追加到 HttpRequest.PathBase。
MapWhen 基于給定謂詞的結(jié)果創(chuàng)建請(qǐng)求管道分支。 Func<HttpContext, bool> 類(lèi)型的任何謂詞均可用于將請(qǐng)求映射到管道的新分支。 在以下示例中,謂詞用于檢測(cè)查詢(xún)字符串變量 branch 是否存在:
C#
public class Startup
{
private static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
public void Configure(IApplicationBuilder app)
{
app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
}
}
下表使用前面的代碼顯示來(lái)自 http://localhost:1234 的請(qǐng)求和響應(yīng)。
請(qǐng)求 | 響應(yīng) |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/?branch=master | Branch used = master |
Map 支持嵌套,例如:
C#
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
此外,Map 還可同時(shí)匹配多個(gè)段:
C#
public class Startup
{
private static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map multiple segments.");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
}
}
ASP.NET Core 附帶以下中間件組件。 “順序”列提供備注,以說(shuō)明中間件在請(qǐng)求處理管道中的放置,以及中間件可能會(huì)終止請(qǐng)求處理的條件。 如果中間件讓請(qǐng)求處理管道短路,并阻止下游中間件進(jìn)一步處理請(qǐng)求,它被稱(chēng)為“終端中間件”。 若要詳細(xì)了解短路,請(qǐng)參閱使用 IApplicationBuilder 創(chuàng)建中間件管道部分。
中間件 | 描述 | 順序 |
---|---|---|
身份驗(yàn)證 | 提供身份驗(yàn)證支持。 | 在需要 HttpContext.User 之前。 OAuth 回叫的終端。 |
Cookie 策略 | 跟蹤用戶(hù)是否同意存儲(chǔ)個(gè)人信息,并強(qiáng)制實(shí)施 cookie 字段(如 secure 和 SameSite )的最低標(biāo)準(zhǔn)。 | 在發(fā)出 cookie 的中間件之前。 示例:身份驗(yàn)證、會(huì)話、MVC (TempData)。 |
CORS | 配置跨域資源共享。 | 在使用 CORS 的組件之前。 |
異常處理 | 處理異常。 | 在生成錯(cuò)誤的組件之前。 |
轉(zhuǎn)接頭 | 將代理標(biāo)頭轉(zhuǎn)發(fā)到當(dāng)前請(qǐng)求。 | 在使用已更新字段的組件之前。 示例:方案、主機(jī)、客戶(hù)端 IP、方法。 |
運(yùn)行狀況檢查 | 檢查 ASP.NET Core 應(yīng)用及其依賴(lài)項(xiàng)的運(yùn)行狀況,如檢查數(shù)據(jù)庫(kù)可用性。 | 如果請(qǐng)求與運(yùn)行狀況檢查終結(jié)點(diǎn)匹配,則為終端。 |
HTTP 方法重寫(xiě) | 允許傳入 POST 請(qǐng)求重寫(xiě)方法。 | 在使用已更新方法的組件之前。 |
HTTPS 重定向 | 將所有 HTTP 請(qǐng)求重定向到 HTTPS(ASP.NET Core 2.1 或更高版本)。 | 在使用 URL 的組件之前。 |
HTTP 嚴(yán)格傳輸安全性 (HSTS) | 添加特殊響應(yīng)標(biāo)頭的安全增強(qiáng)中間件(ASP.NET Core 2.1 或更高版本)。 | 在發(fā)送響應(yīng)之前,修改請(qǐng)求的組件之后。 示例:轉(zhuǎn)接頭、URL 重寫(xiě)。 |
MVC | 用 MVC/Razor Pages 處理請(qǐng)求(ASP.NET Core 2.0 或更高版本)。 | 如果請(qǐng)求與路由匹配,則為終端。 |
OWIN | 與基于 OWIN 的應(yīng)用、服務(wù)器和中間件進(jìn)行互操作。 | 如果 OWIN 中間件處理完請(qǐng)求,則為終端。 |
響應(yīng)緩存 | 提供對(duì)緩存響應(yīng)的支持。 | 在需要緩存的組件之前。 |
響應(yīng)壓縮 | 提供對(duì)壓縮響應(yīng)的支持。 | 在需要壓縮的組件之前。 |
請(qǐng)求本地化 | 提供本地化支持。 | 在對(duì)本地化敏感的組件之前。 |
路由 | 定義和約束請(qǐng)求路由。 | 用于匹配路由的終端。 |
會(huì)話 | 提供對(duì)管理用戶(hù)會(huì)話的支持。 | 在需要會(huì)話的組件之前。 |
靜態(tài)文件 | 為提供靜態(tài)文件和目錄瀏覽提供支持。 | 如果請(qǐng)求與文件匹配,則為終端。 |
URL 重寫(xiě) | 提供對(duì)重寫(xiě) URL 和重定向請(qǐng)求的支持。 | 在使用 URL 的組件之前。 |
WebSockets | 啟用 WebSockets 協(xié)議。 | 在接受 WebSocket 請(qǐng)求所需的組件之前。 |
更多建議: