注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .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
前後執行動作,如下列範例所示:
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) 的預設頁面,以處理中介軟體鏈結中從這裡開始的所有要求 | 鏈結中的晚期,讓用於提供靜態檔案、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
前後執行動作,如下列範例所示:
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 | 您好,來自非地圖的委派。 |
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 時,必須在路由中介軟體之後出現。 |
端點路由 | 定義並限制要求路由。 | 比對路由的終端機。 |
礦泉 | 傳回單一頁面應用程式 (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
前後執行動作,如下列範例所示:
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 的元件之前。
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 中介軟體完全處理了請求,則終止。 |
要求解壓縮 | 提供解壓縮要求的支援。 | 在讀取請求正文的元件之前。 |
回應快取 | 提供快取回應的支援。 | 在需要快取的元件之前。
UseCORS 必須位於 UseResponseCaching 之前。 |
回應壓縮 | 提供壓縮回應的支援。 | 在需要壓縮的元件之前。 |
要求當地語系化 | 提供當地語系化支援。 | 在處理本地化敏感的元件之前。 使用 RouteDataRequestCultureProvider 時,必須在路由中介軟體之後出現。 |
端點路由 | 定義並限制要求路由。 | 比對路由的終端機。 |
礦泉 | 傳回單一頁面應用程式 (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 中要求管線之間的差異,並提供額外的中間件範例。
使用 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 應用程式時,未新增的中介軟體會被註解掉。
- 並非所有中介軟體都會以這個確切的順序出現,但有許多中介軟體都會依這個順序出現。 例如:
在某些情況下,中介軟體有不同的排序。 例如,快取和壓縮的排序依情境而異,而且有多種有效排序方法。 例如:
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 | 您好,來自非地圖的委派。 |
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 之前,由於 這個錯誤。 |
診斷 | 數個不同的中介軟體,可提供開發人員例外狀況頁面、例外狀況處理、狀態字碼頁,以及新應用程式的預設網頁。 | 在產生錯誤的元件之前。 管理例外狀況的終端,或為新應用程式提供預設首頁。 |
轉送標頭 | 將設為 Proxy 的標頭轉送到目前要求。 | 在元件使用更新後的欄位之前。 範例:配置、主機,用戶端 IP、方法。 |
健康狀態檢查 | 檢查 ASP.NET Core 應用程式及其相依性的健康狀態,例如檢查資料庫可用性。 | 如果某個請求符合健康檢查端點,則將終止。 |
標頭傳播 | 將 HTTP 標頭從傳入要求傳播至傳出 HTTP 用戶端要求。 | |
HTTP 方法覆寫 | 允許傳入的 POST 要求覆寫方法。 | 在使用更新方法的元件之前。 |
HTTPS 重新導向 | 將所有 HTTP 要求都重新導向至 HTTPS。 | 在使用 URL 的元件之前。 |
HTTP 嚴格的傳輸安全性 (HSTS) | 增強安全性的中介軟體,可新增特殊的回應標頭。 | 在傳送回應之前,以及在修改要求的元件之後。 範例:轉送的標頭、URL 重寫。 |
MVC | 使用 MVC/Razor Pages 處理要求。 | 如果請求符合路由,則停止操作。 |
OWIN | 以 OWIN 為基礎的應用程式、伺服器和中介軟體的互操作性。 | 如果 OWIN 中介軟體完全處理了請求,則終止。 |
回應快取 | 提供快取回應的支援。 | 在需要快取的元件之前。
UseCORS 必須位於 UseResponseCaching 之前。 |
回應壓縮 | 提供壓縮回應的支援。 | 在需要壓縮的元件之前。 |
要求當地語系化 | 提供當地語系化支援。 | 在處理本地化敏感的元件之前。 使用 RouteDataRequestCultureProvider 時,必須在路由中介軟體之後出現。 |
端點路由 | 定義並限制要求路由。 | 比對路由的終端機。 |
礦泉 | 傳回單一頁面應用程式 (SPA) 的預設頁面,以處理中介軟體鏈結中從這裡開始的所有要求 | 鏈結中的晚期,讓用於提供靜態檔案、MVC 動作等的其他中介軟體優先。 |
會話 | 提供管理使用者工作階段的支援。 | 在需要會話的元件之前。 |
靜態檔案 | 支援靜態檔案的提供和目錄瀏覽。 | 如果請求符合檔案,則終止處理。 |
URL 重寫 | 提供重寫 URL 及重新導向要求的支援。 | 在使用 URL 的元件之前。 |
WebSocket | 啟用 WebSockets 通訊協定。 | 在接受 WebSocket 要求的必要元件之前。 |