ASP.NET Core 中介軟體
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
由 Rick Anderson 與 Steve Smith 撰寫
中介軟體為組成應用程式管線的軟體,用以處理要求與回應。 每個元件:
- 可選擇是否要將要求傳送到管線中的下一個元件。
- 可以在管線中下一個元件的前後執行工作。
要求委派用於建構請求管線。 請求委派會處理每個 HTTP 要求。
要求委派的設定方式為使用 Run、Map 和 Use 擴充方法。 您可將個別要求委派指定為內嵌匿名方法 (在內嵌中介軟體中呼叫),或於可重複使用的類別中加以定義。 這些可重複使用的類別及內嵌匿名方法皆為「中介軟體」,也稱為「中介軟體元件」。 要求管線中的每個中介軟體元件負責調用管線中下一個元件,或中斷管線的執行。 當中介軟體短路時,稱為「終端中介軟體」,因為它會防止接下來的中介軟體處理要求。
將 HTTP 處理常式和模組遷移至 ASP.NET Core 中介軟體說明 ASP.NET Core 和 ASP.NET 4.x 之間的要求管線差異,並提供其他中介軟體範例。
按應用程式類型分析中間件的角色
Blazor Web App、Razor Pages 和 MVC 使用中介軟體在伺服器上處理瀏覽器的請求。 本文中的指引適用於這些類型的應用程式。
獨立 Blazor WebAssembly 應用程式會在用戶端上完全執行,且不會使用中間件管線處理要求。 本文中的指引不適用於獨立 Blazor WebAssembly 應用程式。
中介軟體程式碼分析
ASP.NET Core 包含許多編譯器平台分析器,可檢查應用程式程式碼的品質。 如需詳細資訊,請參閱 ASP.NET Core 中的程式碼分析
使用 WebApplication
建立中介軟體管線
ASP.NET Core 要求管線由要求委派序列組成,並會一個接著一個呼叫。 下圖說明此概念。 執行線程按照黑色箭頭的方向執行。
每一個委派皆能在下個委派的前後執行作業。 處理例外狀況的委派應該在管線中較早呼叫,以便能捕捉到管線後續階段中發生的例外狀況。
最簡潔的 ASP.NET Core 應用程式會設定單一要求委派來處理所有要求。 此情況不包含實際請求管線。 反之,系統會呼叫單一匿名函式來回應每個 HTTP 要求。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
使用 Use 鏈結多個委派請求。
next
參數代表管線中的下個委派。 您可以透過不使用 next
參數來跳過管線的運行。 您通常可以在委派 next
前後執行動作,如下列範例所示:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can 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.");
});
app.Run();
尋找要求管線最短路徑
當委派不將要求傳遞到下一個委派時,這就是所謂中斷要求管線。 短路操作常是理想的,因為它可以避免不必要的運算。 例如,靜態檔案中間件 可以做為 終端中間件,方法是處理靜態檔案的要求,並縮短管線的其餘部分。 在終結進一步處理的中介軟體之前新增到管線的中介軟體仍然會在它們的 next.Invoke
陳述式之後處理程式碼。 不過,請閱覽下列有關嘗試寫入已發出的回應的警告。
警告
請不要在回應傳送至用戶端期間或之後呼叫 next.Invoke
。
HttpResponse 啟動後若有變更,會導致例外狀況。 例如,在回應啟動後設定標頭和狀態碼會拋出例外狀況。 若在呼叫 next
後寫入回應主體:
- 可能會導致通訊協定違規,例如寫入內容超過議定的
Content-Length
。 - 可能會損毀本文格式,例如將 HTML 頁尾寫入至 CSS 檔案。
HasStarted 是實用的提示,可表示是否已傳送標頭 (或) 是否已寫入本文。
如需詳細資訊,請參閱路由後的短路中介軟體。
Run
代表
Run 代理人不會收到 next
參數。 第一個 Run
委派一律是終端機,且會終止管線。
Run
是一種慣例。 有些中介軟體元件可能將執行於管線尾端的 Run[Middleware]
方法公開:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can 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.");
});
app.Run();
如果您想要查看翻譯為英文以外語言的程式碼註解,請在此 GitHub 討論問題中告訴我們。
在上述範例中,Run
委派會將 "Hello from 2nd delegate."
寫入回應,然後終止管線。 如果在 Use
委派之後新增另一個 Run
或 Run
委派,則不會呼叫。
偏好使用需傳遞上下文給下一個處理程序的 app.Use 多載
非配置 app.Use 擴充方法:
- 需要將內容傳遞至
next
。 - 儲存兩個內部個別要求配置,在使用其他多載時需要用到。
如需詳細資訊,請參閱這個 GitHub 問題。
中介軟體順序
下圖顯示 ASP.NET Core MVC 和 Razor Pages 應用程式的完整要求處理管線。 您可以查看一般應用程式中現有中介軟體的排序方式,以及新增自訂中介軟體的位置。 您可以完全控制重新排序現有中介軟體的方式,或視需要插入新的自訂中介軟體,以用於您的案例。
上圖中的 [端點] 中介軟體會針對對應的應用程式類型 (MVC 或 Razor Pages) 執行篩選管線。
上圖中的 [路由] 中介軟體位於 [靜態檔案] 之後。 這是專案範本藉由明確呼叫 app.UseRouting 應用程式所實作的順序。 如果您未呼叫 app.UseRouting
,則 [路由] 中介軟體預設會在管線開頭執行。 如需詳細資訊,請參閱路由。
Program.cs
檔案內中介軟體元件的新增順序可定義在要求時叫用中介軟體元件的順序及回應的反向順序。 對安全性、效能與功能性而言,此順序相當重要。
Program.cs
中的下列醒目提示程式碼會以一般建議的順序新增安全性相關的中介軟體元件:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
在上述程式碼中:
- 使用個別使用者帳戶建立新 Web 應用程式時,未新增的中介軟體會被註解掉。
- 並非所有中介軟體都會以這個確切的順序出現,但有許多中介軟體都會依這個順序出現。 例如:
-
UseCors
、UseAuthentication
和UseAuthorization
必須以顯示的順序出現。 -
UseCors
目前必須出現在UseResponseCaching
之前。 GitHub 問題 dotnet/aspnetcore #23218 中會說明這項需求。 -
UseRequestLocalization
必須出現在可能檢查要求文化特性 (例如app.UseStaticFiles()
) 的任何中介軟體之前。 - 在使用速率限制端點特定 API 時,必須在 UseRateLimiter 之後呼叫
UseRouting
。 例如,如果使用[EnableRateLimiting]
屬性,則必須在UseRateLimiter
之後呼叫UseRouting
。 只呼叫全域限制器時,可以在UseRateLimiter
之前呼叫UseRouting
。
-
在某些情況下,中介軟體有不同的排序。 例如,緩存和壓縮的排序是根據具體情況而定的,而且有多種有效的排序方式。 例如:
app.UseResponseCaching();
app.UseResponseCompression();
使用上述程式碼時,可藉由快取壓縮的回應來減少 CPU 使用量,但您最後可能會使用不同的壓縮演算法 (例如 Gzip 或 Brotli) 來快取資源的多個表示法。
下列順序結合了靜態檔案,可允許快取壓縮的靜態檔案:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
下列 Program.cs
程式碼會新增適用於一般應用程式案例的中介軟體元件:
- 例外狀況/錯誤處理
- 當應用程式在開發環境中執行時:
- 開發人員例外狀況頁面中介軟體 (UseDeveloperExceptionPage) 會回報應用程式執行階段錯誤。
- 資料庫錯誤頁面中介軟體 (UseDatabaseErrorPage) 會回報資料庫執行階段錯誤。
- 當應用程式在生產環境中執行時:
- 例外狀況處理中介軟體 (UseExceptionHandler) 會捕捉在下列中介軟體中擲回的例外狀況。
- HTTP 靜態傳輸安全性通訊協定 (HSTS) 中介軟體 (UseHsts) 會新增
Strict-Transport-Security
標頭。
- 當應用程式在開發環境中執行時:
- HTTPS 重新導向中介軟體 (UseHttpsRedirection) 會將 HTTP 要求重新導向到 HTTPS。
- 靜態檔案中介軟體 (UseStaticFiles) 會傳回靜態檔案並縮短進一步的要求處理時間。
- Cookie 政策中介軟體 (UseCookiePolicy) 會使應用程式符合歐盟一般資料保護規條 (GDPR) 法規。
- 路由中介軟體 (UseRouting) 來路由請求。
- 驗證中介軟體 (UseAuthentication) 會嘗試在允許使用者存取安全資源之前先驗證使用者。
- 授權中介軟體 (UseAuthorization) 可授權使用者存取安全資源。
- 工作階段中介軟體 (UseSession) 會建立並維護工作階段狀態。 若應用程式使用工作階段狀態,請在 Cookie 原則中介軟體之後、MVC 中介軟體之前呼叫工作階段中介軟體。
- 端點路由中介軟體 (UseEndpoints 與 MapRazorPages) 將 Razor Pages 端點新增至要求管線。
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
在上面的範例程式碼中,每個中介軟體擴充方法都會透過 WebApplicationBuilder 命名空間在 Microsoft.AspNetCore.Builder 上公開。
UseExceptionHandler 是第一個新增到管道的中介軟體元件。 因此,例外處理常式中介軟體會攔截後續呼叫中發生的所有例外狀況。
靜態檔案中介軟體會在管線中早期被呼叫,這樣就能處理請求並直接跳過剩餘的元件。 靜態檔案中介軟體不會執行授權檢查。 靜態檔案中介軟體提供的所有檔案 (包括在 wwwroot 下的檔案) 皆可公開使用。 如需保護靜態檔案的方法,請參閱 ASP.NET Core 中的靜態檔案。
若靜態檔案中介軟體未處理要求,該要求會繼續傳遞給執行驗證的驗證中介軟體 (UseAuthentication)。 驗證不會直接跳過未經驗證的請求。 雖然驗證中介軟體會驗證要求,但只有在 MVC 選取特定 Razor Page 或 MVC 控制器及動作後,才會進行驗證 (與拒絕)。
下列範例示範中介軟體的順序,首先由靜態檔案中介軟體處理靜態檔案要求,然後再由回應壓縮中介軟體處理。 靜態檔案並不會以此中介軟體順序壓縮。 可以壓縮 Razor Pages 的回應。
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.MapRazorPages();
如需單頁應用程式的相關資訊,請參閱 ASP.NET Core 中的單一頁面應用程式 (SPA) 概觀。
UseCors 和 UseStaticFiles 的順序
呼叫 UseCors
和 UseStaticFiles
的順序取決於應用程式。 如需詳細資訊,請參閱 UseCors 和 UseStaticFiles 順序
轉發標頭中介軟體的順序
應在其他中介軟體之前執行轉送標頭中介軟體。 這種排序可確保依賴轉送標頭資訊的中介軟體可以耗用用於處理的標頭值。 若要在診斷和錯誤處理中介軟體之後執行轉送標頭中介軟體,請參閱轉送標頭中介軟體順序。
分支中介軟體管線
Map 擴充方法則是用來分支管線的慣例。
Map
會依據指定要求路徑的相符項目將要求管線分支。 如果要求路徑以指定路徑為開頭,則會執行分支。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
下表說明使用上述程式碼後,來自 http://localhost:1234
的要求及回應。
請求 | 回應 |
---|---|
localhost:1234 | 您好,來自非地圖的委派。 |
localhost:1234/map1 | 地圖測試 1 |
localhost:1234/map2 | 地圖測試2 |
localhost:1234/map3 | 非地圖委派者的問候。 |
使用 Map
時,會將相符的路徑線段從 HttpRequest.Path
移除,並附加至每個要求的 HttpRequest.PathBase
。
Map
支援嵌套,例如:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
Map
也可以一次比對多個線段:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen 會根據指定述詞的結果對請求管線進行分支。
Func<HttpContext, bool>
類型的任何述詞皆可用來將要求對應至管線的新分支。 下列範例會使用述詞來偵測查詢字串變數 branch
是否存在:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
下表顯示使用上述程式碼後,來自 http://localhost:1234
的要求及回應:
請求 | 回應 |
---|---|
localhost:1234 |
Hello from non-Map delegate. |
localhost:1234/?branch=main |
Branch used = main |
UseWhen 也會依據指定謂詞的結果將請求管線分支。 與 MapWhen
不同,如果未包含終端中間件,此分支會重新加入主要管線:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
在上述範例中,會針對所有要求撰寫 Hello from non-Map delegate.
的回應。 如果要求包含查詢字串變數 branch
,則會在重新加入主要管線之前記錄其值。
內建的中介軟體
ASP.NET Core 隨附下列中介軟體元件。 「順序」欄提供關於中介軟體在請求處理管道中的位置的註解,以及在何種情況下中介軟體可能終止請求處理。 當中介軟體終止要求處理管線並防止後續的下游中介軟體處理要求時,我們稱之為「終端中介軟體」。 如需關於短路的詳細資訊,請參閱使用 WebApplication 建立中介軟體管線。
中介軟體 | 描述 | 訂單 |
---|---|---|
驗證 | 提供驗證支援。 | 在需要 HttpContext.User 之前。 OAuth 回呼的終端機。 |
授權 | 提供授權支援。 | 緊接在驗證中介軟體之後。 |
Cookie 政策 | 追蹤使用者對用於儲存個人資訊的同意,並強制執行 cookie 欄位的最低標準,例如 secure 和 SameSite 。 |
在發出 Cookie 的中介軟體之前。 範例:驗證、工作階段、MVC (TempData)。 |
CORS | 設定跨原始來源資源共用。 | 在使用 CORS 的元件之前。
UseCors 目前必須在 UseResponseCaching 之前,由於 這個錯誤。 |
DeveloperExceptionPage | 產生頁面,其中包含僅供 [開發] 環境中使用的錯誤資訊。 | 在產生錯誤的元件之前。 當環境為 [開發] 時,專案範本會自動將此中介軟體註冊為管線中的第一個中介軟體。 |
診斷 | 數個不同的中介軟體,可提供開發人員例外狀況頁面、例外狀況處理、狀態字碼頁,以及新應用程式的預設網頁。 | 在產生錯誤的元件之前。 管理例外狀況的終端,或為新應用程式提供預設首頁。 |
轉送標頭 | 將設為 Proxy 的標頭轉送到目前要求。 | 在元件使用更新後的欄位之前。 範例:配置、主機,用戶端 IP、方法。 |
健康狀態檢查 | 檢查 ASP.NET Core 應用程式及其相依性的健康狀態,例如檢查資料庫可用性。 | 如果某個請求符合健康檢查端點,則將終止。 |
標頭傳播 | 將 HTTP 標頭從傳入要求傳播至傳出 HTTP 用戶端要求。 | |
HTTP 記錄 | 記錄 HTTP 要求和回應。 | 中介軟體管線的開頭。 |
HTTP 方法覆寫 | 允許傳入的 POST 要求覆寫方法。 | 在使用更新方法的元件之前。 |
HTTPS 重新導向 | 將所有 HTTP 要求都重新導向至 HTTPS。 | 在使用 URL 的元件之前。 |
HTTP 嚴格的傳輸安全性 (HSTS) | 增強安全性的中介軟體,可新增特殊的回應標頭。 | 在傳送回應之前,以及在修改要求的元件之後。 範例:轉送的標頭、URL 重寫。 |
MVC | 使用 MVC/Razor Pages 處理要求。 | 如果請求符合路由,則停止操作。 |
OWIN | 以 OWIN 為基礎的應用程式、伺服器和中介軟體的互操作性。 | 如果 OWIN 中介軟體完全處理了請求,則終止。 |
輸出快取 | 提供支援以根據設定進行回應快取。 | 在需要快取的元件之前。
UseRouting 必須位於 UseOutputCaching 之前。
UseCORS 必須位於 UseOutputCaching 之前。 |
回應快取 | 提供快取回應的支援。 這需要用戶端參與才能運作。 使用輸出快取來完全控制伺服器。 | 在需要快取的元件之前。
UseCORS 必須位於 UseResponseCaching 之前。 通常對 Razor Pages 等 UI 應用程式沒有益處,因為瀏覽器通常會設定防止快取的請求標頭。
輸出快取 對 UI 應用程式有益。 |
要求解壓縮 | 提供解壓縮要求的支援。 | 在讀取請求正文的元件之前。 |
回應壓縮 | 提供壓縮回應的支援。 | 在需要壓縮的元件之前。 |
要求當地語系化 | 提供當地語系化支援。 | 在處理本地化敏感的元件之前。 使用 RouteDataRequestCultureProvider 時,必須在路由中介軟體之後出現。 |
要求逾時 | 提供設置請求超時的支援,包括全域設定和針對每個端點的設定。 |
UseRequestTimeouts 必須在 UseExceptionHandler 、UseDeveloperExceptionPage 和 UseRouting 後面。 |
端點路由 | 定義並限制要求路由。 | 比對路由的終端機。 |
SPA | 傳回單一頁面應用程式 (SPA) 的預設頁面,以處理中介軟體鏈結中從這裡開始的所有要求 | 鏈結中的晚期,讓用於提供靜態檔案、MVC 動作等的其他中介軟體優先。 |
會話 | 提供管理使用者工作階段的支援。 | 在需要會話的元件之前。 |
靜態檔案 | 支援靜態檔案的提供和目錄瀏覽。 | 如果請求符合檔案,則終止處理。 |
URL 重寫 | 提供重寫 URL 及重新導向要求的支援。 | 在使用 URL 的元件之前。 |
W3CLogging | 以 W3C 擴充記錄檔格式產生伺服器存取記錄。 | 中介軟體流程的起始點。 |
WebSocket | 啟用 WebSockets 通訊協定。 | 在接受 WebSocket 要求的必要元件之前。 |
其他資源
由 Rick Anderson 與 Steve Smith 撰寫
中介軟體為組成應用程式管線的軟體,用以處理要求與回應。 每個元件:
- 可選擇是否要將要求傳送到管線中的下一個元件。
- 可以在管道中下一個元件的前後執行工作。
請求委託物件用於構建請求管道。 要求委派處理每個 HTTP 要求。
要求委派的設定方式為使用 Run、Map 和 Use 擴充方法。 您可以將個別請求委派指定為內嵌匿名方法(作為內嵌中介軟體呼叫),或在可重複使用的類別中定義。 這些可重複使用的類別及內嵌匿名方法皆為「中介軟體」,也稱為「中介軟體元件」。 在請求管線中的每個中介軟體元件皆負責調用管線中的下一個元件,或者對管線進行短路。 當中介軟體短路時,稱為「終端中介軟體」,因為它會防止接下來的中介軟體處理要求。
將 HTTP 處理常式和模組遷移至 ASP.NET Core 中介軟體說明 ASP.NET Core 和 ASP.NET 4.x 之間的要求管線差異,並提供其他中介軟體範例。
按應用程式類型分析中間件的角色
Razor Pages、MVC、Blazor Server,以及裝載的 Blazor WebAssembly 解決方案的伺服器端專案使用中間件來在伺服器上處理瀏覽器請求。 本文中的指引適用於這些類型的應用程式。
獨立 Blazor WebAssembly 應用程式會在用戶端上完全執行,且不會使用中間件管線處理要求。 本文中的指引不適用於獨立 Blazor WebAssembly 應用程式。
中介軟體程式碼分析
ASP.NET Core 包含許多編譯器平台分析器,可檢查應用程式程式碼的品質。 如需詳細資訊,請參閱 ASP.NET Core 中的程式碼分析
使用 WebApplication
建立中介軟體管線
ASP.NET Core 要求管線由一連串的要求委派組成,按順序依次呼叫。 下圖說明此概念。 執行緒按照黑色箭號的方向運行。
每一個委派皆能在下個委派的前後執行作業。 應在管線的早期階段呼叫處理例外狀況的委派函式,以便能夠捕捉管線後續階段中發生的例外狀況。
最簡潔的 ASP.NET Core 應用程式會設定單一要求委派來處理所有要求。 此案例不包含實際的請求通道。 反之,系統會呼叫單一匿名函式來回應每個 HTTP 要求。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
將多個請求委派用 Use 串聯起來。
next
參數代表管線中的下個委派。 您可以不呼叫 next
參數來使管線短路。 您通常可以在 next
委派的前後執行動作,如下列範例所示:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can 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.");
});
app.Run();
當委派不將要求傳遞到下一個委派時,這就是所謂中斷要求管線。 短路通常是可取的,因為它可以避免不必要的工作。 例如,靜態檔案中間件 可以做為 終端中間件,方法是處理靜態檔案的要求,並縮短管線的其餘部分。 在終結進一步處理的中介軟體之前新增到管線的中介軟體,仍然會在其 next.Invoke
陳述式之後執行程式碼。 不過,請查看下列有關嘗試修改已傳送回應的警告。
警告
請不要在回應已傳送給用戶端之後呼叫 next.Invoke
。 回應啟動後,變更為 HttpResponse 會擲回例外狀況。 例如,設定標頭和狀態碼等都會擲回例外狀況。 在呼叫 next
之後寫入回應內容:
- 可能導致違反通訊協定。 例如,寫入超過指定
Content-Length
的內容。 - 可能損毀本文格式。 例如,將 HTML 頁尾寫入 CSS 檔。
HasStarted 是一個實用的提示,可以指示標頭是否已傳送或本文是否已寫入。
Run 委派不會收到 next
參數。 第一個 Run
委派一律是終端機,且會終止管線。
Run
是一種慣例。 有些中介軟體元件可能將執行於管線尾端的 Run[Middleware]
方法公開:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can 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.");
});
app.Run();
如果您想要查看翻譯為英文以外語言的程式碼註解,請在此 GitHub 討論問題中告訴我們。
在上述範例中,Run
委派會將 "Hello from 2nd delegate."
寫入回應,然後終止管線。 如果在 Use
委派之後新增另一個 Run
或 Run
委派,則不會呼叫。
偏好需要將內容傳遞至下一個的 app.Use 多載
不進行記憶體分配的 app.Use 擴充方法:
- 需要將內容傳遞至
next
。 - 節省在使用其他重載時所需的兩個每次請求的內部配置。
如需詳細資訊,請參閱這個 GitHub 問題。
中介軟體順序
下圖顯示 ASP.NET Core MVC 和 Razor Pages 應用程式的完整要求處理管線。 您可以查看一般應用程式中現有中介軟體的排序方式,以及新增自訂中介軟體的位置。 您可以完全控制重新排序現有中介軟體的方式,或視需要插入新的自訂中介軟體,以用於您的案例。
上圖中的 [端點] 中介軟體會針對對應的應用程式類型 (MVC 或 Razor Pages) 執行篩選管線。
上圖中,[路由] 中介軟體顯示在 [靜態檔案] 之後。 這是專案範本藉由明確呼叫 app.UseRouting 應用程式所實作的順序。 如果您未呼叫 app.UseRouting
,則 [路由] 中介軟體預設會在管線開頭執行。 如需詳細資訊,請參閱路由。
Program.cs
檔案內中介軟體元件的新增順序可定義在要求時叫用中介軟體元件的順序及回應的反向順序。 對安全性、效能與功能性而言,此順序相當重要。
Program.cs
中的下列醒目提示程式碼會以一般建議的順序新增安全性相關的中介軟體元件:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
在上述程式碼中:
- 使用個別使用者帳戶建立新 Web 應用程式時,未包含的中介軟體會被註解掉。
- 並非所有中介軟體都會以這個確切的順序出現,但有許多中介軟體都會依這個順序出現。 例如:
-
UseCors
、UseAuthentication
和UseAuthorization
必須以顯示的順序出現。 -
UseCors
目前必須出現在UseResponseCaching
之前。 GitHub 問題 dotnet/aspnetcore #23218 中會說明這項需求。 -
UseRequestLocalization
必須位於任何可能檢查請求文化特性(例如app.UseStaticFiles()
)的中介軟體之前。 - 在使用速率限制端點特定 API 時,必須在 UseRateLimiter 之後呼叫
UseRouting
。 例如,如果使用[EnableRateLimiting]
屬性,則必須在UseRateLimiter
之後呼叫UseRouting
。 只呼叫全域限制器時,可以在UseRateLimiter
之前呼叫UseRouting
。
-
在某些情況下,中介軟體有不同的排序。 例如,快取和壓縮排序因應各種情境而定,而且有多個有效的排序。 例如:
app.UseResponseCaching();
app.UseResponseCompression();
使用上述程式碼時,可藉由快取壓縮的回應來減少 CPU 使用量,但您最後可能會使用不同的壓縮演算法 (例如 Gzip 或 Brotli) 來快取資源的多個表示法。
下列順序結合了靜態檔案,可允許快取壓縮的靜態檔案:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
下列 Program.cs
程式碼會新增適用於一般應用程式案例的中介軟體元件:
- 例外狀況/錯誤處理
- 當應用程式在開發環境中執行時:
- 開發人員例外狀況頁面中介軟體 (UseDeveloperExceptionPage) 會回報應用程式執行階段錯誤。
- 資料庫錯誤頁面中介軟體 (UseDatabaseErrorPage) 會回報資料庫執行階段錯誤。
- 當應用程式在生產環境中執行時:
- 例外處理中介軟體 (UseExceptionHandler) 會捕捉在下列中介軟體中擲回的例外。
- HTTP 靜態傳輸安全性通訊協定 (HSTS) 中介軟體 (UseHsts) 會新增
Strict-Transport-Security
標頭。
- 當應用程式在開發環境中執行時:
- HTTPS 重新導向中介軟體 (UseHttpsRedirection) 會將 HTTP 要求重新導向到 HTTPS。
- 靜態檔案中介軟體 (UseStaticFiles) 會傳回靜態檔案並縮短進一步的要求處理時間。
- Cookie 政策中介軟體 (UseCookiePolicy) 會使應用程式符合歐盟一般資料保護規則 (GDPR) 法規。
- 路由中介軟體 (UseRouting) 用於路由請求。
- 驗證中介軟體 (UseAuthentication) 會嘗試在允許使用者存取安全資源之前先驗證使用者。
- 授權中介軟體 (UseAuthorization) 可授權使用者存取安全資源。
- 工作階段中介軟體 (UseSession) 會建立並維護工作階段狀態。 若應用程式使用工作階段狀態,請在 Cookie 原則中介軟體之後、MVC 中介軟體之前呼叫工作階段中介軟體。
- 端點路由中介軟體 (UseEndpoints 與 MapRazorPages) 將 Razor Pages 端點新增至要求管線。
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
在上面的範例程式碼中,每個中介軟體擴充方法都會透過 WebApplicationBuilder 命名空間在 Microsoft.AspNetCore.Builder 上公開。
UseExceptionHandler 是第一個新增到管道的中介軟體元件。 因此,例外處理常式中介件會攔截後續呼叫中發生的所有例外狀況。
靜態檔案中介軟體會提早在處理管線中被呼叫,以便直接處理請求並跳過剩餘的元件。 靜態檔案中介軟體不會執行授權檢查。 靜態檔案中介軟體提供的所有檔案 (包括在 wwwroot 下的檔案) 皆可公開使用。 如需保護靜態檔案的方法,請參閱 ASP.NET Core 中的靜態檔案。
若靜態檔案中介軟體未處理要求,該要求會繼續傳遞給執行驗證的驗證中介軟體 (UseAuthentication)。 驗證不會中斷未經驗證的要求。 雖然驗證中介軟體會驗證要求,但只有在 MVC 選取特定 Razor Page 或 MVC 控制器及動作後,才會進行驗證 (與拒絕)。
下列範例展示了一個中介軟體的順序,其中對靜態檔案的請求由靜態檔案中介軟體優先處理,而後再由回應壓縮中介軟體處理。 靜態檔案並不會以此中介軟體順序壓縮。 可以壓縮 Razor Pages 回應。
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.MapRazorPages();
如需單頁應用程式的相關資訊,請參閱 React 和 Angular 專案範本的指南。
UseCors 和 UseStaticFiles 順序
呼叫 UseCors
和 UseStaticFiles
的順序取決於應用程式。 如需詳細資訊,請參閱 UseCors 和 UseStaticFiles 順序
轉發標頭中介軟體順序
應在其他中介軟體之前執行轉送標頭中介軟體。 這種排序可確保依賴轉送標頭資訊的中介軟體可以耗用用於處理的標頭值。 若要在診斷和錯誤處理中介軟體之後執行轉送標頭中介軟體,請參閱轉送標頭中介軟體順序。
將中介軟體流程進行分流
Map 擴充功能被用作分支管線的慣例。
Map
根據給定的要求路徑匹配來分支要求管線。 如果要求路徑以指定路徑為開頭,則會執行分支。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
下表說明使用上述程式碼後,來自 http://localhost:1234
的要求及回應。
要求 | 回應 |
---|---|
localhost:1234 | 您好,來自非 Map 的代表。 |
localhost:1234/map1 | 地圖測試1 |
localhost:1234/map2 | 地圖測試2 |
localhost:1234/map3 | 您好,來自非 Map 委派的問候。 |
使用 Map
時,會將相符的路徑線段從 HttpRequest.Path
移除,並附加至每個要求的 HttpRequest.PathBase
。
Map
支援巢狀結構,例如:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
Map
也可以一次比對多個線段:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen 會根據指定謂詞的結果將請求流程分支。
Func<HttpContext, bool>
類型的任何述詞皆可用來將要求對應至管線的新分支。 下列範例會使用述詞來偵測查詢字串變數 branch
是否存在:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
下表顯示使用上述程式碼後,來自 http://localhost:1234
的要求及回應:
請求 | 回應 |
---|---|
localhost:1234 |
Hello from non-Map delegate. |
localhost:1234/?branch=main |
Branch used = main |
UseWhen 也會依據指定條件的結果將請求管線分支。 不同於使用 MapWhen
,如果分支沒有中途終止或不包含終端中介軟體,則此分支會重新合併到主要管線。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
在上述範例中,會針對所有要求撰寫 Hello from non-Map delegate.
的回應。 如果要求包含查詢字串變數 branch
,則會在重新加入主要管線之前記錄其值。
內建的中介軟體
ASP.NET Core 隨附下列中介軟體元件。 「順序欄」提供了有關中介軟體在請求處理管線中的定位,以及在什麼情況下中介軟體可能會終止請求處理的說明。 當一個中介軟體攔截要求處理管線並阻止任何後續的下游中介軟體處理該要求時,這被稱為「終端中介軟體」。 如需關於短路評估的詳細資訊,請參閱使用 WebApplication 建立中介軟體管線。
中介軟體 | 描述 | 順序 |
---|---|---|
驗證 | 提供驗證支援。 | 在需要 HttpContext.User 之前。 OAuth 回呼的終端機。 |
授權 | 提供授權支援。 | 緊接在驗證中介軟體之後。 |
Cookie 政策 | 追蹤使用者對用於儲存個人資訊的同意,並強制執行 cookie 欄位的最低標準,例如 secure 和 SameSite 。 |
在發出 Cookie 的中介軟體之前。 範例:驗證、工作階段、MVC (TempData)。 |
CORS | 設定跨原始來源資源共用。 | 在使用 CORS 的元件之前。
UseCors 目前必須在 UseResponseCaching 之前,原因是 這個錯誤。 |
DeveloperExceptionPage | 生成僅供開發環境中使用的錯誤資訊頁面。 | 在產生錯誤的元件之前。 當環境為 [開發] 時,專案範本會自動將此中介軟體註冊為管線中的第一個中介軟體。 |
診斷 | 數個不同的中介軟體,可提供開發人員例外狀況頁面、例外狀況處理、狀態字碼頁,以及新應用程式的預設網頁。 | 在會出錯的元件之前。 用於處理例外狀況或提供新應用程式的預設網頁的終端。 |
轉送標頭 | 將設為 Proxy 的標頭轉送到目前要求。 | 在消耗更新欄位的元件之前。 範例:配置、主機,用戶端 IP、方法。 |
健康狀態檢查 | 檢查 ASP.NET Core 應用程式及其相依性的健康狀態,例如檢查資料庫可用性。 | 若某項要求與健康檢查端點相符,則會終止。 |
標頭傳播 | 將 HTTP 標頭從傳入要求傳播至傳出 HTTP 用戶端要求。 | |
HTTP 記錄 | 記錄 HTTP 要求和回應。 | 中介軟體管線的初始階段。 |
HTTP 方法覆寫 | 允許傳入的 POST 要求覆寫方法。 | 在使用更新方法的元件之前。 |
HTTPS 重新導向 | 將所有 HTTP 要求都重新導向至 HTTPS。 | 在使用 URL 的元件之前。 |
HTTP 嚴格的傳輸安全性 (HSTS) | 增強安全性的中介軟體,可新增特殊的回應標頭。 | 在傳送回應之前,以及在修改要求的元件之後。 範例:轉送的標頭、URL 重寫。 |
MVC | 使用 MVC/Razor Pages 處理要求。 | 若要求符合路由則終止。 |
OWIN | 與基於 OWIN 的應用程式、伺服器和中介軟體的互操作性。 | 如果 OWIN 中介軟體完全處理請求,則結束操作。 |
輸出快取 | 提供根據設定快取回應的支援。 | 在需要快取的元件之前。
UseRouting 必須位於 UseOutputCaching 之前。
UseCORS 必須位於 UseOutputCaching 之前。 |
回應快取 | 提供快取回應的支援。 這需要用戶端參與才能運作。 使用輸出快取以完全控制伺服器。 | 在需要快取的元件之前。
UseCORS 必須位於 UseResponseCaching 之前。 通常對於 UI 應用程式,例如 Razor Pages,並不有利,因為瀏覽器通常會設定要求標頭,以阻止快取。
輸出快取 對 UI 應用程式有益。 |
要求解壓縮 | 提供解壓縮要求的支援。 | 在讀取請求體的元件之前。 |
回應壓縮 | 提供壓縮回應的支援。 | 在需要壓縮的元件之前。 |
要求當地語系化 | 提供當地語系化支援。 | 在偵測當地語系化的元件之前。 使用 RouteDataRequestCultureProvider 時,必須在路由中介軟體之後出現。 |
端點路由 | 定義並限制要求路由。 | 比對路由的終端機。 |
SPA | 傳回單一頁面應用程式 (SPA) 的預設頁面,以處理中介軟體鏈結中從這裡開始的所有要求 | 在執行順序的後段,以便用於提供靜態檔案、MVC 動作等的其他中介軟體優先處理。 |
工作階段 | 提供管理使用者會話的支援。 | 在需要會話的元件之前。 |
靜態檔案 | 支援靜態檔案的提供和目錄瀏覽。 | 若要求符合檔案,則終止動作。 |
URL 重寫 | 提供重寫 URL 及重新導向要求的支援。 | 在使用 URL 的元件之前。 |
W3CLogging | 以 W3C 擴充記錄檔格式產生伺服器存取記錄。 | 中介軟體管線的開頭。 |
WebSocket | 啟用 WebSockets 通訊協定。 | 在需要接受 WebSocket 請求的元件之前。 |
其他資源
由 Rick Anderson 與 Steve Smith 撰寫
中介軟體為組成應用程式管線的軟體,用以處理要求與回應。 每個元件:
- 可選擇是否要將要求傳送到管線中的下一個元件。
- 可以在流程中的下一個元件之前和之後執行工作。
請求委派用於構建請求管線。 請求委派會處理每個 HTTP 要求。
要求委派是透過使用 Run、Map 和 Use 擴充方法來設定的。 您可將單個請求委派指定為內聯匿名方法 (在內聯中介軟體中呼叫),或定義在可重用的類別中。 這些可重複使用的類別及內嵌匿名方法皆為「中介軟體」,也稱為「中介軟體元件」。 在要求管線中,每個中介軟體元件都負責調用管線中的下一個元件,或者直接終止管線的執行。 當中介軟體短路時,稱為「終端中介軟體」,因為它會防止接下來的中介軟體處理要求。
將 HTTP 處理常式和模組遷移至 ASP.NET Core 中介軟體說明 ASP.NET Core 和 ASP.NET 4.x 之間的要求管線差異,並提供其他中介軟體範例。
中介軟體程式碼分析
ASP.NET Core 包含許多編譯器平台分析器,可檢查應用程式程式碼的品質。 如需詳細資訊,請參閱 ASP.NET Core 中的程式碼分析
使用 WebApplication
建立中介軟體管線
ASP.NET Core 要求管線由要求委派序列組成,並會依序呼叫。 下圖說明此概念。 執行緒依循黑色箭頭的方向進行。
每個代理程式可以在下一個代理程式之前或之後執行操作。 處理例外狀況的委派應該在管線的早期階段呼叫,以便可以攔截管線後續階段中發生的例外狀況。
最簡潔的 ASP.NET Core 應用程式會設定單一要求委派來處理所有要求。 此案例不包含實際的請求處理管道。 反之,系統會呼叫單一匿名函式來回應每個 HTTP 要求。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
將多個要求委派鏈結在一起的方法是使用 Use。
next
參數代表管線中的下一個委派物件。 您可以不呼叫next
參數來中斷管線。 您通常可以在委派 next
之前或之後執行動作,如下列範例所示:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can 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.");
});
app.Run();
當委派不將要求傳遞到下一個委派時,這就是所謂讓要求管線短路。 短路運算常常受到青睞,因為它可以避免不必要的工作。 例如,靜態檔案中間件 可以做為 終端中間件,方法是處理靜態檔案的要求,並縮短管線的其餘部分。 在中介軟體之前新增到管線且終結進一步處理的中介軟體在其 next.Invoke
陳述式之後仍然處理程式碼。 請查看下列有關試圖寫入已傳送回應的警告。
警告
請不要在回應已傳送給用戶端之後呼叫 next.Invoke
。 在回應啟動後,變更 HttpResponse 會擲回例外狀況。 例如,設定標頭和狀態碼等都會擲回例外狀況。 若在呼叫 next
後寫入回應本文:
- 可能導致違反通訊協定。 例如,寫入超過指定
Content-Length
的內容。 - 可能損毀本文格式。 例如,將 HTML 頁尾寫入 CSS 檔。
HasStarted 是一個實用的提示,用於指示是否已傳送標頭或已寫入本文。
Run 委派函式不會收到 next
參數。 第一個 Run
委派一律是終端機,且會終止管線。
Run
是一種慣例。 有些中介軟體元件可能將執行於管線尾端的 Run[Middleware]
方法公開:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can 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.");
});
app.Run();
如果您想要查看翻譯為英文以外語言的程式碼註解,請在此 GitHub 討論問題中告訴我們。
在上述範例中,Run
委派會將 "Hello from 2nd delegate."
寫入回應,然後終止管線。 如果在 Use
委派之後新增另一個 Run
或 Run
委派,則不會呼叫。
偏好使用需要將上下文傳遞至下一個的 app.Use 多載函數
無額外配置 app.Use 擴充方法:
- 需要將內容傳遞至
next
。 - 儲存兩個內部個別要求配置,在使用其他多載時需要用到。
如需詳細資訊,請參閱這個 GitHub 問題。
中間件順序
下圖顯示 ASP.NET Core MVC 和 Razor Pages 應用程式的完整要求處理管線。 您可以查看一般應用程式中現有中介軟體的排序方式,以及新增自訂中介軟體的位置。 您可以完全控制重新排序現有中介軟體的方式,或視需要插入新的自訂中介軟體,以用於您的案例。
上圖中的 [端點] 中介軟體會針對對應的應用程式類型 (MVC 或 Razor Pages) 執行篩選管線。
上述圖表中的 [路由] 中介軟體顯示在 [靜態檔案] 之後。 這是專案範本藉由明確呼叫 app.UseRouting 應用程式所實作的順序。 如果您未呼叫 app.UseRouting
,則 [路由] 中介軟體預設會在管線開頭執行。 如需詳細資訊,請參閱路由。
Program.cs
檔案內中介軟體元件的新增順序可定義在要求時叫用中介軟體元件的順序及回應的反向順序。 對安全性、效能與功能性而言,此順序相當重要。
Program.cs
中的下列醒目提示程式碼會以一般建議的順序新增安全性相關的中介軟體元件:
using IndividualAccountsExample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
在上述程式碼中:
- 使用個別使用者帳戶建立新 Web 應用程式時,未新增的中介軟體會加以註解化。
- 並非所有中介軟體都會以這個確切的順序出現,但有許多中介軟體都會依這個順序出現。 例如:
-
UseCors
、UseAuthentication
和UseAuthorization
必須以顯示的順序出現。 -
UseCors
目前必須出現在UseResponseCaching
之前。 GitHub 問題 dotnet/aspnetcore #23218 中會說明這項需求。 -
UseRequestLocalization
必須出現在可能檢查請求的文化特性 (例如app.UseMvcWithDefaultRoute()
) 的任何中介軟體之前。
-
在某些情況下,中介軟體有不同的排序。 例如,快取和壓縮的排序取決於具體情境,而且有多種有效的排序方式。 例如:
app.UseResponseCaching();
app.UseResponseCompression();
使用上述程式碼時,可藉由快取壓縮的回應來減少 CPU 使用量,但您最後可能會使用不同的壓縮演算法 (例如 Gzip 或 Brotli) 來快取資源的多個表示法。
下列排序將靜態檔案結合起來,以便於快取已壓縮的靜態檔案:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
下列 Program.cs
程式碼會新增適用於一般應用程式案例的中介軟體元件:
- 例外狀況/錯誤處理
- 當應用程式在開發環境中執行時:
- 開發人員例外狀況頁面中介軟體 (UseDeveloperExceptionPage) 會回報應用程式執行階段錯誤。
- 資料庫錯誤頁面中介軟體 (UseDatabaseErrorPage) 會報告資料庫執行階段錯誤。
- 當應用程式在生產環境中執行時:
- 例外狀況處理程序中介軟體 (UseExceptionHandler) 會捕捉在下列中介軟體中拋出的例外狀況。
- HTTP 靜態傳輸安全性通訊協定 (HSTS) 中介軟體 (UseHsts) 會新增
Strict-Transport-Security
標頭。
- 當應用程式在開發環境中執行時:
- HTTPS 重新導向中介軟體 (UseHttpsRedirection) 會將 HTTP 要求重新導向到 HTTPS。
- 靜態檔案中介軟體 (UseStaticFiles) 會傳回靜態檔案並縮短進一步的要求處理時間。
- Cookie 政策中介軟體 (UseCookiePolicy) 使應用程式遵從歐盟一般資料保護條例 (GDPR) 法規。
- 路由中介軟體 (UseRouting) 來路由請求。
- 驗證中介軟體 (UseAuthentication) 會嘗試在允許使用者存取安全資源之前先驗證使用者。
- 授權中介軟體 (UseAuthorization) 可授權使用者存取安全資源。
- 工作階段中介軟體 (UseSession) 會建立並維護工作階段狀態。 若應用程式使用工作階段狀態,請在 Cookie 原則中介軟體之後、MVC 中介軟體之前呼叫工作階段中介軟體。
- 端點路由中介軟體 (UseEndpoints 與 MapRazorPages) 將 Razor Pages 端點新增至要求管線。
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
在上面的範例程式碼中,每個中介軟體擴充方法都會透過 WebApplicationBuilder 命名空間在 Microsoft.AspNetCore.Builder 上公開。
UseExceptionHandler 是第一個新增到管道的中介軟體元件。 因此,例外處理常式中介程式會攔截後續呼叫中發生的所有例外狀況。
靜態檔案中介軟體會提前在管線中呼叫,以便其無須逐一處理剩餘的元件,就能處理要求及執行最少運算。 靜態檔案中介軟體不會執行授權檢查。 靜態檔案中介軟體提供的所有檔案 (包括在 wwwroot 下的檔案) 皆可公開使用。 如需保護靜態檔案的方法,請參閱 ASP.NET Core 中的靜態檔案。
若靜態檔案中介軟體未處理要求,該要求會繼續傳遞給執行驗證的驗證中介軟體 (UseAuthentication)。 驗證不會跳過未經驗證的請求。 雖然驗證中介軟體會驗證要求,但只有在 MVC 選取特定 Razor Page 或 MVC 控制器及動作後,才會進行驗證 (與拒絕)。
在下列範例中,靜態檔案中介軟體在回應壓縮中介軟體之前處理靜態檔案要求,顯示中介軟體的順序。 靜態檔案並不會以此中介軟體順序壓縮。 可以壓縮來自 Razor Pages 的回應。
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.MapRazorPages();
如需單頁應用程式的相關資訊,請參閱 React 和 Angular 專案範本的指南。
UseCors 和 UseStaticFiles 順序
呼叫 UseCors
和 UseStaticFiles
的順序取決於應用程式。 如需詳細資訊,請參閱 UseCors 和 UseStaticFiles 順序
轉發標頭中介軟體的順序
轉送標頭中介軟體應在其他中介軟體之前運行。 這種排序可確保依賴轉送標頭資訊的中介軟體可以耗用用於處理的標頭值。 若要在診斷和錯誤處理中介軟體之後執行轉送標頭中介軟體,請參閱轉送標頭中介軟體順序。
中介軟體管線分支化
Map 擴充方法則是用來分支管線的慣例。
Map
會根據符合的要求路徑來分支要求管線。 如果要求路徑以指定路徑為開頭,則會執行分支。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
下表說明使用上述程式碼後,來自 http://localhost:1234
的要求及回應。
請求 | 回應 |
---|---|
localhost:1234 | 您好,來自非映射代表的問候。 |
localhost:1234/map1 | 地圖測試1 |
localhost:1234/map2 | 地圖測試 2 |
localhost:1234/map3 | 來自非地圖代理的問候。 |
使用 Map
時,會將相符的路徑線段從 HttpRequest.Path
移除,並附加至每個要求的 HttpRequest.PathBase
。
Map
支援巢狀結構,例如:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
Map
也可以一次比對多個線段:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen 會依據指定述詞的結果將要求管線分支。
Func<HttpContext, bool>
類型的任何述詞皆可用來將要求對應至管線的新分支。 下列範例會使用述詞來偵測查詢字串變數 branch
是否存在:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
下表顯示使用上述程式碼後,來自 http://localhost:1234
的要求及回應:
請求 | 回應 |
---|---|
localhost:1234 |
Hello from non-Map delegate. |
localhost:1234/?branch=main |
Branch used = main |
UseWhen 也會依據指定述詞的結果將要求管線分支。 不同於使用 MapWhen
,如果該分支沒有提前結束或不包含終端中介件,它將重新加入主要管線。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
在上述範例中,會針對所有要求撰寫 Hello from non-Map delegate.
的回應。 如果要求包含查詢字串變數 branch
,則會在重新加入主要管線之前記錄其值。
內建的中介軟體
ASP.NET Core 隨附下列中介軟體元件。 「順序」欄提供中介軟體在要求處理管線中的位置資訊,以及中介軟體可能終止要求處理的條件。 當中介軟體中斷請求處理流程並防止後續的中介軟體處理請求時,這就是所謂的「終端中介軟體」。 如需關於短路的詳細資訊,請參閱使用 WebApplication 建立中介軟體管線。
中介軟體 | 描述 | 訂單 |
---|---|---|
驗證 | 提供驗證支援。 | 在需要 HttpContext.User 之前。 OAuth 回呼的終端機。 |
授權 | 提供授權支援。 | 緊接在驗證中介軟體之後。 |
Cookie 政策 | 追蹤使用者對用於儲存個人資訊的同意,並強制執行 cookie 欄位的最低標準,例如 secure 和 SameSite 。 |
在發出 Cookie 的中介軟體之前。 範例:驗證、工作階段、MVC (TempData)。 |
CORS | 設定跨原始來源資源共用。 | 在使用 CORS 的元件之前。
UseResponseCaching 目前必須在 UseCors 之前,因為此錯誤。 |
DeveloperExceptionPage | 生成一個頁面,其中包含僅供開發環境中使用的錯誤資訊。 | 在產生錯誤的元件之前。 當環境為 [開發] 時,專案範本會自動將此中介軟體註冊為管線中的第一個中介軟體。 |
診斷 | 數個不同的中介軟體,可提供開發人員例外狀況頁面、例外狀況處理、狀態字碼頁,以及新應用程式的預設網頁。 | 在產生錯誤的元件之前。 例外狀況的終端機,或為新應用程式提供預設網頁。 |
轉送標頭 | 將設為 Proxy 的標頭轉送到目前要求。 | 在使用已更新欄位的元件之前。 範例:配置、主機,用戶端 IP、方法。 |
健康狀態檢查 | 檢查 ASP.NET Core 應用程式及其相依性的健康狀態,例如檢查資料庫可用性。 | 若某項要求與健康狀態檢查端點相符,則該過程將會終止。 |
標頭傳播 | 將 HTTP 標頭從傳入要求傳播至傳出 HTTP 用戶端要求。 | |
HTTP 記錄 | 記錄 HTTP 要求和回應。 | 中介軟體管線的開頭。 |
HTTP 方法重寫 | 允許傳入的 POST 要求覆寫方法。 | 在使用更新方法的元件之前。 |
HTTPS 重新導向 | 將所有 HTTP 要求都重新導向至 HTTPS。 | 在使用或處理 URL 的元件之前。 |
HTTP 嚴格的傳輸安全性 (HSTS) | 增強安全性的中介軟體,可新增特殊的回應標頭。 | 在傳送回應之前,以及在修改要求的元件之後。 範例:轉送的標頭、URL 重寫。 |
MVC | 使用 MVC/Razor Pages 處理要求。 | 要求符合路由時終止。 |
OWIN | 與基於 OWIN 的應用程式、伺服器及中介軟體進行互操作。 | 若 OWIN 中介軟體完全處理請求,則終止處理。 |
要求解壓縮 | 提供解壓縮要求的支援。 | 在讀取要求本文的元件之前。 |
回應緩存 | 支援快取回應。 | 在需要快取的元件之前。
UseCORS 必須位於 UseResponseCaching 之前。 |
回應壓縮 | 提供壓縮回應的支援。 | 在需要壓縮的元件之前。 |
要求當地語系化 | 提供當地語系化支援。 | 在處理對當地語系化敏感的元件之前。 使用 RouteDataRequestCultureProvider 時,必須在路由中介軟體之後出現。 |
端點路由 | 定義並限制要求路由。 | 比對路由的終端機。 |
SPA | 傳回單一頁面應用程式 (SPA) 的預設頁面,以處理中介軟體鏈結中從這裡開始的所有要求 | 鏈結中的晚期,讓用於提供靜態檔案、MVC 動作等的其他中介軟體優先。 |
工作階段 | 提供管理使用者工作階段的支援。 | 在需要 Session 的元件之前。 |
靜態檔案 | 支援靜態檔案的提供和目錄瀏覽。 | 若請求符合檔案,則程序終止。 |
URL 重寫 | 提供重寫 URL 及重新導向要求的支援。 | 在使用 URL 的元件之前。 |
W3CLogging | 以 W3C 擴充記錄檔格式產生伺服器存取記錄。 | 在中介軟體管線的起始點。 |
WebSocket | 啟用 WebSockets 通訊協定。 | 在接受 WebSocket 要求的必要元件之前。 |
其他資源
由 Rick Anderson 與 Steve Smith 撰寫
中介軟體為組成應用程式管線的軟體,用以處理要求與回應。 每個元件:
- 可選擇是否要將要求傳送到管線中的下一個元件。
- 可以在流程中下一個元件的前後執行工作。
要求委派用於建置請求管道。 請求代理會處理每個 HTTP 要求。
要求委派是透過使用 Run、Map 和 Use 擴充方法來設定的。 您可將個別請求的委派指定為內嵌匿名方法(稱為內嵌中介軟體),或將其定義於可重複使用的類別中。 這些可重複使用的類別及內嵌匿名方法皆為「中介軟體」,也稱為「中介軟體元件」。 要求管線中的每個中介件元件負責叫用管線中下一個元件,或中斷管線。 當中介軟體短路時,稱為「終端中介軟體」,因為它會防止接下來的中介軟體處理要求。
將 HTTP 處理常式和模組遷移至 ASP.NET Core 中介軟體說明 ASP.NET Core 和 ASP.NET 4.x 之間的要求管線差異,並提供其他中介軟體範例。
使用 IApplicationBuilder 建立中介軟體管線
ASP.NET Core 要求管線由要求委派序列組成,並會一個接著一個呼叫。 下圖說明此概念。 執行緒遵循黑色箭號執行。
每位代表可以在下一位代表之前和之後執行操作。 處理例外狀況的委派應在管線的早期階段呼叫,以便能夠捕捉在管線後面階段發生的例外狀況。
最簡潔的 ASP.NET Core 應用程式會設定單一要求委派來處理所有要求。 此情況不包含實際請求管線。 反之,系統會呼叫單一匿名函式來回應每個 HTTP 要求。
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});
}
}
將多個要求委派鏈結在一起的方法是使用 Use。
next
參數代表管線中的下個委派。 您可以藉由「不」呼叫 「next」參數來中斷管線。 您通常可以在下個委派的前後執行動作,如下列範例所示:
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.");
});
}
}
當委派不將要求傳遞到下一個委派時,這就是所謂截斷請求管道。 短路求值常常是值得的,因為它可以避免不必要的工作。 例如,靜態檔案中間件 可以做為 終端中間件,方法是處理靜態檔案的要求,並縮短管線的其餘部分。 在終止進一步處理的中介軟體前新增至管線的中介軟體,在其next.Invoke
陳述式後仍然繼續處理程式碼。 不過,請參閱下列有關嘗試寫入已傳送的回應時的警告。
警告
請不要在回應已傳送給用戶端之後呼叫 next.Invoke
。 在回應開始後對 HttpResponse 的變更會擲回例外狀況。 例如,設定標頭和狀態碼時會拋出例外。 若在呼叫 next
後將內容寫入回應本文:
- 可能導致違反通訊協定。 例如,寫入超過指定
Content-Length
的內容。 - 可能損毀本文格式。 例如,將 HTML 頁尾寫入 CSS 檔。
HasStarted 是實用的提示,可表示是否已傳送標頭 (或) 是否已寫入本文。
Run 委派函式不會收到 next
參數。 第一個 Run
委派一律是終端,且會終止管線。
Run
是一種慣例。 有些中介軟體元件可能將執行於管線尾端的 Run[Middleware]
方法公開:
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.");
});
}
}
如果您想要查看翻譯為英文以外語言的程式碼註解,請在此 GitHub 討論問題中告訴我們。
在上述範例中,Run
委派會將 "Hello from 2nd delegate."
寫入回應,然後終止管線。 如果在 Use
委派之後新增另一個 Run
或 Run
委派,則不會呼叫。
中介軟體順序
下圖顯示 ASP.NET Core MVC 和 Razor Pages 應用程式的完整要求處理管線。 您可以查看一般應用程式中現有中介軟體的排序方式,以及新增自訂中介軟體的位置。 您可以完全控制重新排序現有中介軟體的方式,或視需要插入新的自訂中介軟體,以用於您的案例。
上圖中的 [端點] 中介軟體會針對對應的應用程式類型 (MVC 或 Razor Pages) 執行篩選管線。
Startup.Configure
方法內中介軟體元件的新增順序可定義在要求時叫用中介軟體元件的順序及回應的反向順序。 對安全性、效能與功能性而言,此順序相當重要。
下列 Startup.Configure
方法會以一般建議的順序新增安全性相關的中介軟體元件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
在上述程式碼中:
- 使用個別使用者帳戶建立新 Web 應用程式時,未新增的中介軟體會被註解掉。
- 並非所有中介軟體都會以這個確切的順序出現,但有許多中介軟體都會依這個順序出現。 例如:
-
UseCors
、UseAuthentication
和UseAuthorization
必須以顯示的順序出現。 - 由於這個問題,
UseCors
目前必須在UseResponseCaching
之前顯示。 - 必須出現在任何可能檢查請求文化資訊的中介軟體之前,例如
UseRequestLocalization
和app.UseMvcWithDefaultRoute()
。
-
在某些情況下,中介軟體有不同的排序。 例如,快取和壓縮的排序依情境而異,而且有多種有效排序方法。 例如:
app.UseResponseCaching();
app.UseResponseCompression();
使用上述程式碼時,可藉由快取壓縮的回應來節省 CPU 使用,但您可能會最終使用不同的壓縮演算法,例如 Gzip 或 Brotli,來快取資源的多個表示形式。
下列順序結合了靜態檔案,可允許快取壓縮的靜態檔案:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
下列 Startup.Configure
方法會新增適用於一般應用程式案例的中介軟體元件:
- 例外狀況/錯誤處理
- 當應用程式在開發環境中執行時:
- 開發人員例外狀況頁面中介軟體 (UseDeveloperExceptionPage) 會回報應用程式執行階段錯誤。
- 資料庫錯誤頁面中介軟體會報告資料庫執行階段錯誤。
- 當應用程式在生產環境中執行時:
- 例外處理程式中介軟體 (UseExceptionHandler) 會捕捉在下列中介軟體中擲回的例外狀況。
- HTTP 靜態傳輸安全性通訊協定 (HSTS) 中介軟體 (UseHsts) 會新增
Strict-Transport-Security
標頭。
- 當應用程式在開發環境中執行時:
- HTTPS 重新導向中介軟體 (UseHttpsRedirection) 會將 HTTP 要求重新導向到 HTTPS。
- 靜態檔案中介軟體 (UseStaticFiles) 會傳回靜態檔案並縮短進一步的要求處理時間。
- Cookie 政策中介軟體 (UseCookiePolicy) 將應用程式符合歐盟一般資料保護法規 (GDPR) 。
- 路由中介軟體 (UseRouting) 用於路由請求。
- 驗證中介軟體 (UseAuthentication) 會嘗試在允許使用者存取安全資源之前先驗證使用者。
- 授權中介軟體 (UseAuthorization) 可授權使用者存取安全資源。
- 工作階段中介軟體 (UseSession) 會建立並維護工作階段狀態。 若應用程式使用工作階段狀態,請在 Cookie 原則中介軟體之後、MVC 中介軟體之前呼叫工作階段中介軟體。
- 端點路由中介軟體 (UseEndpoints 與 MapRazorPages) 將 Razor Pages 端點新增至要求管線。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
在上面的範例程式碼中,每個中介軟體擴充方法都會透過 IApplicationBuilder 命名空間在 Microsoft.AspNetCore.Builder 上公開。
UseExceptionHandler 是第一個新增到管道的中介軟體元件。 因此,例外處理中介軟體會攔截在後續呼叫中發生的所有例外狀況。
靜態檔案中介軟體在管線中早期被呼叫,以便能直接處理請求和實現捷徑,無需遍歷其餘元件。 靜態檔案中介軟體不會執行授權檢查。 靜態檔案中介軟體提供的所有檔案 (包括在 wwwroot 下的檔案) 皆可公開使用。 如需保護靜態檔案的方法,請參閱 ASP.NET Core 中的靜態檔案。
若靜態檔案中介軟體未處理要求,該要求會繼續傳遞給執行驗證的驗證中介軟體 (UseAuthentication)。 驗證不會繞過未經驗證的請求。 雖然驗證中介軟體會驗證要求,但只有在 MVC 選取特定 Razor Page 或 MVC 控制器及動作後,才會進行驗證 (與拒絕)。
以下範例顯示了一個中介軟體處理順序,其中靜態檔案要求由靜態檔案中介軟體先於回應壓縮中介軟體處理。 靜態檔案並不會以此中介軟體順序壓縮。 Razor Pages 所生成的回應可以被壓縮。
public void Configure(IApplicationBuilder app)
{
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
針對單頁應用程式 (SPA),SPA 中介軟體 UseSpaStaticFiles 通常在中介軟體管線中最後出現。 SPA 中介軟體位於最後:
- 允許所有其他中介軟體先回應相符的要求。
- 允許具有用戶端路由的 SPA 針對伺服器應用程式無法辨識的所有路由執行。
如需 SPA 的更多詳細資料,請參閱 React 和 Angular 專案範本的指南。
轉送標頭中介軟體順序
轉送標頭中介軟體應在其他中介軟體之前執行。 這種排序可確保依賴轉送標頭資訊的中介軟體可以耗用用於處理的標頭值。 若要在診斷和錯誤處理中介軟體之後執行轉送標頭中介軟體,請參閱轉送標頭中介軟體順序。
將中介軟體管線分支
Map 擴展通常用於作為分支管線的慣例。
Map
會依據指定要求路徑的相符項目將要求管線分支。 如果要求路徑以指定路徑為開頭,則會執行分支。
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.");
});
}
}
下表說明使用上述程式碼後,來自 http://localhost:1234
的要求及回應。
請求 | 回應 |
---|---|
localhost:1234 | 來自非 Map 代理的問候 |
localhost:1234/map1 | 地圖測試1 |
localhost:1234/map2 | 地圖測試 2 |
localhost:1234/map3 | 來自非地圖代表的問候。 |
使用 Map
時,會將相符的路徑線段從 HttpRequest.Path
移除,並附加至每個要求的 HttpRequest.PathBase
。
Map
支援巢狀結構,例如:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
Map
也可以一次比對多個線段:
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.");
});
}
}
MapWhen 會根據指定謂詞的結果來分支請求管線。
Func<HttpContext, bool>
類型的任何述詞皆可用來將要求對應至管線的新分支。 下列範例會使用述詞來偵測查詢字串變數 branch
是否存在:
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.");
});
}
}
下表顯示使用上述程式碼後,來自 http://localhost:1234
的要求及回應:
請求 | 回應 |
---|---|
localhost:1234 | 來自非地圖授權者的問候。 |
localhost:1234/?branch=main | 使用的分支 = main |
UseWhen 也會根據指定條件的結果將請求管線分支。 不同於使用 MapWhen
,如果分支沒有短路或包含終端中介軟體,則此分支會重新加入主要管線:
public class Startup
{
private void HandleBranchAndRejoin(IApplicationBuilder app, ILogger<Startup> logger)
{
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder, logger));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from main pipeline.");
});
}
}
在上述範例中,會針對所有要求寫入「Hello from main pipeline.」的回應。 如果要求包含查詢字串變數 branch
,則會在重新加入主要管線之前記錄其值。
內建的中介軟體
ASP.NET Core 隨附下列中介軟體元件。 「順序」欄目提供了中介軟體在請求處理管道中的位置說明,以及中介軟體可能終止請求處理的條件。 當中介軟體中斷請求處理流程並防止後續的中介軟體處理請求時,這就是所謂的「終端中介軟體」。 如需有關短路的更多資訊,請參閱使用 IApplicationBuilder 建立中介軟體管線一節。
中介軟體 | 描述 | 訂單 |
---|---|---|
驗證 | 提供驗證支援。 | 在需要 HttpContext.User 之前。 OAuth 回呼的終端機。 |
授權 | 提供授權支援。 | 緊接在驗證中介軟體之後。 |
Cookie 政策 | 追蹤使用者對用於儲存個人資訊的同意,並強制執行 cookie 欄位的最低標準,例如 secure 和 SameSite 。 |
在發出 Cookie 的中介軟體之前。 範例:驗證、工作階段、MVC (TempData)。 |
CORS | 設定跨原始來源資源共用。 | 在使用 CORS 的元件之前。 由於這個漏洞,UseCors 目前必須在 UseResponseCaching 之前。 |
診斷 | 數個不同的中介軟體,可提供開發人員例外狀況頁面、例外狀況處理、狀態字碼頁,以及新應用程式的預設網頁。 | 在產生錯誤的元件之前。 用於處理例外情況的終端,或用於為新應用程式提供預設網頁。 |
轉送標頭 | 將代理標頭轉發到當前請求。 | 在使用更新欄位的元件之前。 範例:配置、主機,用戶端 IP、方法。 |
健康狀態檢查 | 檢查 ASP.NET Core 應用程式及其相依性的健康狀態,例如檢查資料庫可用性。 | 若某項要求符合健康檢查端點,則會終止。 |
標頭傳播 | 將 HTTP 標頭從傳入要求傳播至傳出 HTTP 用戶端要求。 | |
HTTP 方法覆寫 | 允許傳入的 HTTP POST 請求覆寫方法。 | 在使用更新方法的元件之前。 |
HTTPS 重新導向 | 將所有 HTTP 要求都重新導向至 HTTPS。 | 在處理 URL 的元件之前。 |
HTTP 嚴格的傳輸安全性 (HSTS) | 增強安全性的中介軟體,可新增特殊的回應標頭。 | 在傳送回應前和修改要求的元件後。 範例:轉送的標頭、URL 重寫。 |
MVC | 使用 MVC/Razor Pages 處理要求。 | 若要求符合路由則終止。 |
OWIN | 與基於 OWIN 的應用程式、伺服器和中介軟體進行互操作。 | 若 OWIN 中介軟體已完全處理該請求,則終止。 |
回應快取 | 提供快取回應的支援。 | 在需要快取的元件之前。
UseCORS 必須位於 UseResponseCaching 之前。 |
回應壓縮 | 提供壓縮回應的支援。 | 在需要壓縮的元件之前。 |
要求當地語系化 | 提供當地語系化支援。 | 在本地化敏感元件之前。 使用 RouteDataRequestCultureProvider 時,必須在路由中介軟體之後出現。 |
端點路由 | 定義並限制要求路由。 | 比對路由的終端機。 |
SPA | 傳回單一頁面應用程式 (SPA) 的預設頁面,以處理中介軟體鏈結中從這裡開始的所有要求 | 在處理流程的後期,以便其他用於提供靜態檔案、MVC 操作等的中介軟體具有優先權。 |
會議 | 提供管理使用者會話的支援。 | 在需要工作階段的元件之前。 |
靜態檔案 | 支援靜態檔案的提供和目錄瀏覽。 | 若要求符合檔案則終端。 |
URL 重寫 | 提供重寫 URL 及重新導向要求的支援。 | 在使用 URL 的元件之前。 |
WebSocket | 啟用 WebSockets 通訊協定。 | 在處理 WebSocket 請求之前需要的組件。 |