ASP.NET Core 中介軟體

Rick AndersonSteve Smith 撰寫

中介軟體為組成應用程式管線的軟體,用以處理要求與回應。 每個元件:

  • 可選擇是否要將要求傳送到管線中的下一個元件。
  • 可以下一個元件的前後執行工作。

要求委派用於建置要求管線, 其會處理每個 HTTP 要求。

要求委派的設定方式為使用 RunMapUse 擴充方法。 您可將個別要求委派指定為內嵌匿名方法 (在內嵌中介軟體中呼叫),或於可重複使用的類別中加以定義。 這些可重複使用的類別及內嵌匿名方法皆為「中介軟體」,也稱為「中介軟體元件」。 要求管線中的每個中介軟體元件負責叫用管線中下一個元件,或對管線執行最少運算。 當中介軟體短路時,稱為「終端中介軟體」,因為它會防止接下來的中介軟體處理要求。

將 HTTP 處理常式和模組遷移至 ASP.NET Core 中介軟體 說明 ASP.NET Core 和 ASP.NET 4.x 中要求管線之間的差異,並提供其他中介軟體範例。

中介軟體程式碼分析

ASP.NET Core 包含許多編譯器平臺分析器,可檢查應用程式程式碼的品質。 如需詳細資訊,請參閱 ASP.NET Core 應用程式中的程式碼分析

使用 建立中介軟體管線 WebApplication

ASP.NET Core 要求管線由要求委派序列組成,並會一個接著一個呼叫。 下圖說明此概念。 執行緒遵循黑色箭號執行。

Request processing pattern showing a request arriving, processing through three middlewares, and the response leaving the app. Each middleware runs its logic and hands off the request to the next middleware at the next() statement. After the third middleware processes the request, the request passes back through the prior two middlewares in reverse order for additional processing after their next() statements before leaving the app as a response to the client.

每一個委派皆能在下個委派的前後執行作業。 處理例外狀況的委派必須提前在管線中呼叫,以便其可與管線後續階段中所發生的例外狀況達成一致。

最簡潔的 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();

將多個要求委派鏈結在一起的方法是使用 Usenext 參數代表管線中的下個委派。 您可以藉由 呼叫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." 寫入回應,然後終止管線。 如果在委派之後 Run 新增另一個 UseRun 委派,則不會呼叫它。

偏好應用程式。使用需要將內容傳遞至下一個的多載

未配置 的應用程式。使用 擴充方法:

  • 需要將內容傳遞至 next
  • 儲存使用其他多載時所需的兩個內部個別要求配置。

如需詳細資訊,請參閱這個 GitHub 問題 \(英文\)。

中介軟體順序

下圖顯示 ASP.NET Core MVC 和 Razor Pages 應用程式的完整要求處理管線。 您可以在一般應用程式中查看現有中介軟體的排序方式,以及新增自訂中介軟體的位置。 您可以完全控制如何重新排序現有的中介軟體,或視需要插入新的自訂中介軟體。

ASP.NET Core middleware pipeline

上圖中的 端點 中介軟體會針對對應的應用程式類型執行篩選管線, MVC 或 Razor Pages。

上圖中的 路由 中介軟體會顯示下列 靜態檔案。 這是專案範本藉由明確呼叫應用程式所實作的順序 。UseRouting。 如果您未呼叫 app.UseRouting路由 中介軟體預設會在管線的開頭執行。 如需詳細資訊,請參閱路由

ASP.NET Core filter pipeline

在檔案中 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 應用程式時未新增的中介軟體會標記為批註。
  • 並非所有中介軟體都會以這個確切的順序出現,但有許多中介軟體。 例如:
    • UseCorsUseAuthenticationUseAuthorization 必須依顯示的順序顯示。
    • UseCors 目前必須出現在 之前 UseResponseCachingGitHub 問題 dotnet/aspnetcore #23218會說明這項需求。
    • UseRequestLocalization 必須出現在任何可能檢查要求文化特性 (的中介軟體之前, app.UseMvcWithDefaultRoute() 例如) 。

在某些情況下,中介軟體有不同的排序方式。 例如,快取和壓縮排序是特定案例,而且有多個有效的排序。 例如:

app.UseResponseCaching();
app.UseResponseCompression();

使用上述程式碼時,您可以快取壓縮的回應來減少 CPU 使用量,但最終可能會使用不同的壓縮演算法快取資源的多個標記法,例如 Gzip 或 Brotli。

下列順序結合了靜態檔案,以允許快取壓縮的靜態檔案:

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

下列 Program.cs 程式碼會新增常見應用程式案例的中介軟體元件:

  1. 例外狀況/錯誤處理
    • 當應用程式在開發環境中執行時:
    • 當應用程式在生產環境中執行時:
      • 例外狀況處理常式中介軟體 (UseExceptionHandler) 會攔截在下列中介軟體中擲回的例外狀況。
      • HTTP 靜態傳輸安全性通訊協定 (HSTS) 中介軟體 (UseHsts) 會新增 Strict-Transport-Security 標頭。
  2. HTTPS 重新導向中介軟體 (UseHttpsRedirection) 會將 HTTP 要求重新導向到 HTTPS。
  3. 靜態檔案中介軟體 (UseStaticFiles) 會傳回靜態檔案並縮短進一步的要求處理時間。
  4. Cookie 原則中介軟體 (UseCookiePolicy) 符合歐盟一般資料保護規定 (GDPR) 法規。
  5. 路由中介軟體 (UseRouting) 路由要求。
  6. 驗證中介軟體 (UseAuthentication) 會嘗試在允許使用者存取安全資源之前先驗證使用者。
  7. 授權中介軟體 (UseAuthorization) 授權使用者存取安全資源。
  8. 工作階段中介軟體 (UseSession) 會建立並維護工作階段狀態。 如果應用程式使用會話狀態,請在原則中介軟體和 Cookie MVC 中介軟體之前呼叫會話中介軟體。
  9. 端點路由中介軟體 (, UseEndpoints) MapRazorPages 將 Pages 端點新增 Razor 至要求管線。
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();

在上面的範例程式碼中,每個中介軟體擴充方法都會透過 Microsoft.AspNetCore.Builder 命名空間在 WebApplicationBuilder 上公開。

UseExceptionHandler 是第一個新增到管道的中介軟體元件。 因此,例外處理常式中介軟體會攔截後續呼叫中發生的所有例外狀況。

靜態檔案中介軟體會提前在管線中呼叫,以便其無須逐一處理剩餘的元件,就能處理要求及執行最少運算。 靜態檔案中介軟體 不會 提供授權檢查。 靜態檔案中介軟體提供的任何檔案,包括 wwwroot底下的檔案皆可供公開使用。 如需保護靜態檔案的方法,請參閱 ASP.NET Core 中的靜態檔案

若靜態檔案中介軟體未處理要求,該要求會繼續傳遞給執行驗證的驗證中介軟體 (UseAuthentication)。 驗證不會對未經驗證的要求執行最少運算。 雖然驗證中介軟體會驗證要求,但只有在 MVC 選取特定的 Razor Page 或 MVC 控制器和動作之後,才會發生授權 (和拒絕) 。

下列範例示範靜態檔案中介軟體在回應壓縮中介軟體之前處理靜態檔案要求之前的靜態檔案順序。 靜態檔案並不會以此中介軟體順序壓縮。 Razor頁面回應可以壓縮。

// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

如需單頁應用程式的相關資訊,請參閱 ReactAngular 專案範本的指南。

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. <p>");
});

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 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 2
localhost:1234/map3 Hello from non-Map delegate.

使用 Map 時,會將相符的路徑線段從 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. <p>");
});

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. <p>");
});

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. <p>");
});

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 隨附下列中介軟體元件。 「順序」欄說明 中介軟體在要求處理管線中的位置,以及中介軟體可終止要求處理的情況。 當中介軟體將要求處理管線短路並防止接下來的下游中介軟體處理要求時,這就是所謂的「終端中介軟體」。 如需詳細資訊,請參閱使用 IApplicationBuilder 建立中介軟體管線

中介軟體 描述
驗證 提供驗證支援。 在需要 HttpContext.User 之前。 OAuth 回呼的終端機。
授權 提供授權支援。 緊接在驗證中介軟體之後。
Cookie 政策 追蹤使用者的同意以儲存個人資訊,並強制執列欄位的最低標準 cookie ,例如 secureSameSite 在發生問題的 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 頁面處理要求。 若要求符合路由則終止。
OWIN 以 OWIN 為基礎之應用程式、伺服器和中介軟體的 Interop。 若 OWIN 中介軟體完全處理要求則終止。
回應快取 提供快取回應的支援。 在需要快取的元件之前。 UseCORS 必須位於 之前 UseResponseCaching
回應壓縮 提供壓縮回應的支援。 在需要壓縮的元件之前。
要求當地語系化 提供當地語系化支援。 在偵測當地語系化的元件之前。 使用 RouteDataRequestCultureProvider 時,必須在路由中介軟體之後出現。
端點路由 定義並限制要求路由。 比對路由的終端機。
SPA 傳回單一頁面應用程式的預設頁面,以處理中介軟體鏈結中的所有要求 (SPA) 鏈結中晚期,讓其他用於提供靜態檔案、MVC 動作等的中介軟體優先。
工作階段 提供管理使用者工作階段的支援。 在需要工作階段的元件之前。
靜態檔案 支援靜態檔案的提供和目錄瀏覽。 若要求符合檔案則終止。
URL 重寫 提供重寫 URL 及重新導向要求的支援。 在使用 URL 的元件之前。
W3CLogging W3C 擴充記錄檔格式產生伺服器存取記錄。 中介軟體管線的開頭。
WebSocket 啟用 WebSockets 通訊協定。 在接受 WebSocket 要求的必要元件之前。

其他資源

Rick AndersonSteve Smith 撰寫

中介軟體為組成應用程式管線的軟體,用以處理要求與回應。 每個元件:

  • 可選擇是否要將要求傳送到管線中的下一個元件。
  • 可以下一個元件的前後執行工作。

要求委派用於建置要求管線, 其會處理每個 HTTP 要求。

要求委派的設定方式為使用 RunMapUse 擴充方法。 您可將個別要求委派指定為內嵌匿名方法 (在內嵌中介軟體中呼叫),或於可重複使用的類別中加以定義。 這些可重複使用的類別及內嵌匿名方法皆為「中介軟體」,也稱為「中介軟體元件」。 要求管線中的每個中介軟體元件負責叫用管線中下一個元件,或對管線執行最少運算。 當中介軟體短路時,稱為「終端中介軟體」,因為它會防止接下來的中介軟體處理要求。

將 HTTP 處理常式和模組移轉至 ASP.NET Core 中介軟體 說明 ASP.NET Core 和 ASP.NET 4.x 中的要求管線之間的差異,並提供額外的中介軟體範例。

使用 IApplicationBuilder 建立中介軟體管線

ASP.NET Core 要求管線由要求委派序列組成,並會一個接著一個呼叫。 下圖說明此概念。 執行緒遵循黑色箭號執行。

Request processing pattern showing a request arriving, processing through three middlewares, and the response leaving the app. Each middleware runs its logic and hands off the request to the next middleware at the next() statement. After the third middleware processes the request, the request passes back through the prior two middlewares in reverse order for additional processing after their next() statements before leaving the app as a response to the client.

每一個委派皆能在下個委派的前後執行作業。 處理例外狀況的委派必須提前在管線中呼叫,以便其可與管線後續階段中所發生的例外狀況達成一致。

最簡潔的 ASP.NET Core 應用程式會設定單一要求委派來處理所有要求。 此情況不包含實際要求管線。 反之,系統會呼叫單一匿名函式來回應每個 HTTP 要求。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

將多個要求委派鏈結在一起的方法是使用 Usenext 參數代表管線中的下個委派。 您可以「不」呼叫「下一個」參數來對管線執行最少運算。 您通常可以在下個委派的前後執行動作,如下列範例所示:

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." 寫入回應,然後終止管線。 如果在委派之後 Run 新增另一個 UseRun 委派,則不會呼叫。

中介軟體順序

下圖顯示 ASP.NET Core MVC 和 Razor Pages 應用程式的完整要求處理管線。 您可以在一般應用程式中查看現有中介軟體的排序方式,以及新增自訂中介軟體的位置。 您可以完全控制如何重新排序現有的中介軟體,或視需要插入新的自訂中介軟體。

ASP.NET Core middleware pipeline

上圖中的 端點 中介軟體會針對對應的應用程式類型執行篩選管線, MVC 或 Razor Pages。

ASP.NET Core filter pipeline

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 應用程式時未新增的中介軟體會標記為批註。
  • 並非所有中介軟體都會以這個確切的順序出現,但有許多中介軟體。 例如:
    • UseCorsUseAuthenticationUseAuthorization 必須依顯示的順序顯示。
    • UseCors 目前必須出現之前 UseResponseCaching ,因為 這個錯誤
    • UseRequestLocalization 必須出現在任何可能檢查要求文化特性 (的中介軟體之前, app.UseMvcWithDefaultRoute() 例如) 。

在某些情況下,中介軟體有不同的排序方式。 例如,快取和壓縮排序是特定案例,而且有多個有效的排序。 例如:

app.UseResponseCaching();
app.UseResponseCompression();

使用上述程式碼時,CPU 可以藉由快取壓縮的回應來儲存,但您最終可能會使用 Gzip 或 Brotli 等不同壓縮演算法快取資源的多個標記法。

下列順序結合了靜態檔案,以允許快取壓縮的靜態檔案:

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

下列 Startup.Configure 方法會新增適用於一般應用程式案例的中介軟體元件:

  1. 例外狀況/錯誤處理
    • 當應用程式在開發環境中執行時:
      • 開發人員例外狀況頁面中介軟體 (UseDeveloperExceptionPage) 會回報應用程式執行階段錯誤。
      • 資料庫錯誤頁面中介軟體會報告資料庫執行時間錯誤。
    • 當應用程式在生產環境中執行時:
      • 例外狀況處理常式中介軟體 (UseExceptionHandler) 會攔截在下列中介軟體中擲回的例外狀況。
      • HTTP 靜態傳輸安全性通訊協定 (HSTS) 中介軟體 (UseHsts) 會新增 Strict-Transport-Security 標頭。
  2. HTTPS 重新導向中介軟體 (UseHttpsRedirection) 會將 HTTP 要求重新導向到 HTTPS。
  3. 靜態檔案中介軟體 (UseStaticFiles) 會傳回靜態檔案並縮短進一步的要求處理時間。
  4. Cookie 原則中介軟體 (UseCookiePolicy) 符合歐盟一般資料保護規定 (GDPR) 法規。
  5. 路由中介軟體 (UseRouting) 路由要求。
  6. 驗證中介軟體 (UseAuthentication) 會嘗試在允許使用者存取安全資源之前先驗證使用者。
  7. 授權中介軟體 (UseAuthorization) 授權使用者存取安全資源。
  8. 工作階段中介軟體 (UseSession) 會建立並維護工作階段狀態。 如果應用程式使用會話狀態,請在原則中介軟體和 Cookie MVC 中介軟體之前呼叫會話中介軟體。
  9. 端點路由中介軟體 (, UseEndpoints) MapRazorPages 將 Pages 端點新增 Razor 至要求管線。
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();
    });
}

在上面的範例程式碼中,每個中介軟體擴充方法都會透過 Microsoft.AspNetCore.Builder 命名空間在 IApplicationBuilder 上公開。

UseExceptionHandler 是第一個新增到管道的中介軟體元件。 因此,例外處理常式中介軟體會攔截後續呼叫中發生的所有例外狀況。

靜態檔案中介軟體會提前在管線中呼叫,以便其無須逐一處理剩餘的元件,就能處理要求及執行最少運算。 靜態檔案中介軟體 不會 提供授權檢查。 靜態檔案中介軟體提供的任何檔案,包括 wwwroot底下的檔案皆可供公開使用。 如需保護靜態檔案的方法,請參閱 ASP.NET Core 中的靜態檔案

若靜態檔案中介軟體未處理要求,該要求會繼續傳遞給執行驗證的驗證中介軟體 (UseAuthentication)。 驗證不會對未經驗證的要求執行最少運算。 雖然驗證中介軟體會驗證要求,但只有在 MVC 選取特定的 Razor Page 或 MVC 控制器和動作之後,才會發生授權 (和拒絕) 。

下列範例示範靜態檔案中介軟體在回應壓縮中介軟體之前處理靜態檔案要求之前的靜態檔案順序。 靜態檔案並不會以此中介軟體順序壓縮。 Razor頁面回應可以壓縮。

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 的詳細資訊,請參閱 ReactAngular 專案範本的指南。

轉送標頭中介軟體順序

轉送標頭中介軟體應該在其他中介軟體之前執行。 這種排序可確保依賴轉送標頭資訊的中介軟體可以耗用用於處理的標頭值。 若要在診斷和錯誤處理中介軟體之後執行轉送標頭 中介軟體,請參閱轉送標頭中介軟體順序

將中介軟體管線分支

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. <p>");
        });
    }
}

下表說明使用上述程式碼後,來自 http://localhost:1234 的要求及回應。

要求 回應
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 2
localhost:1234/map3 Hello from non-Map delegate.

使用 Map 時,會將相符的路徑線段從 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. <p>");
        });
    }
}

下表顯示使用上述程式碼後,來自 http://localhost:1234 的要求及回應:

要求 回應
localhost:1234 Hello from non-Map delegate.
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 ,例如 secureSameSite 在發行 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 頁面處理要求。 若要求符合路由則終止。
OWIN 以 OWIN 為基礎之應用程式、伺服器和中介軟體的 Interop。 若 OWIN 中介軟體完全處理要求則終止。
回應快取 提供快取回應的支援。 在需要快取的元件之前。 UseCORS 必須位於 之前 UseResponseCaching
回應壓縮 提供壓縮回應的支援。 在需要壓縮的元件之前。
要求當地語系化 提供當地語系化支援。 在偵測當地語系化的元件之前。 使用 RouteDataRequestCultureProvider 時,必須在路由中介軟體之後出現。
端點路由 定義並限制要求路由。 比對路由的終端機。
SPA 傳回單一頁面應用程式的預設頁面,以處理中介軟體鏈結中的所有要求 (SPA) 鏈結中晚期,讓其他用於提供靜態檔案、MVC 動作等的中介軟體優先。
工作階段 提供管理使用者工作階段的支援。 在需要工作階段的元件之前。
靜態檔案 支援靜態檔案的提供和目錄瀏覽。 若要求符合檔案則終止。
URL 重寫 提供重寫 URL 及重新導向要求的支援。 在使用 URL 的元件之前。
WebSocket 啟用 WebSocket 通訊協定。 在接受 WebSocket 要求的必要元件之前。

其他資源