Middleware ASP.NET Core

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Autor: Rick Anderson a Steve Smith

Middleware je software sestavený do kanálu aplikace pro zpracování požadavků a odpovědí. Každá komponenta:

  • Určuje, jestli se má požadavek předat další komponentě v kanálu.
  • Může provádět práci před a za další komponentou v kanálu.

K sestavení kanálu požadavků se používají delegáti požadavků. Delegáti požadavků zpracovávají každý požadavek HTTP.

Delegáti požadavků se konfigurují pomocí rozšiřujících metod Run, Map a Use. Jednotlivý delegát požadavku může být zadán na řádku jako anonymní metoda (tzv. in-line middleware) nebo může být definován v opakovaně použitelné třídě. Tyto opakovaně použitelné třídy a vložené anonymní metody jsou middleware, označované také jako komponenty middlewaru. Každá komponenta middlewaru v kanálu požadavků zodpovídá za vyvolání další komponenty v kanálu nebo zkratování kanálu. Když middleware zkratuje, označuje se jako terminální middleware, protože brání dalšímu middlewaru ve zpracování požadavku.

Téma věnované migraci obslužných rutin HTTP a modulů do middlewaru ASP.NET Core vysvětluje rozdíl mezi kanály požadavků v prostředích ASP.NET Core a ASP.NET 4.x a poskytuje další ukázky middlewaru.

Analýza kódu middlewaru

ASP.NET Core obsahuje celou řadu analyzátorů Compile Platform, které kontrolují kód aplikace z hlediska kvality. Další informace najdete v tématu Analýza kódu v aplikacích ASP.NET Core.

Vytvoření kanálu middlewaru s využitím WebApplication

Kanál požadavků ASP.NET Core se skládá z posloupnosti delegátů požadavků, kteří se volají jeden po druhém. Tento koncept demonstruje následující schéma. Vlákno spouštění postupuje podle černých šipek.

Vzor zpracování požadavku zobrazující příchozí požadavek, jeho zpracování třemi middlewary a odpověď opouštějící aplikaci. Každý middleware spustí svou logiku a předá požadavek dalšímu middlewaru v příkazu next(). Poté, co třetí middleware zpracuje požadavek, projde požadavek zpět přes předchozí dva middlewary v opačném pořadí k dalšímu zpracování po příkazech next() a poté opustí aplikaci jako odpověď klientovi.

Každý delegát může provádět operace před a za dalším delegátem. Delegáti zpracování výjimek by se měli volat v rané fázi zpracování kanálu, aby mohli zachytit výjimky, ke kterým dochází v pozdějších fázích zpracování kanálu.

Nejjednodušší možná aplikace ASP.NET Core nastavuje jednoho delegáta požadavků, který zpracovává všechny požadavky. Tento případ nezahrnuje vlastní kanál požadavků. Místo toho se jako reakce na každý požadavek HTTP volá jedna anonymní funkce.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Run();

Zřetězte několik delegátů požadavků společně s Use. Parametr next představuje dalšího delegáta v kanálu. Kanál můžete zkratovat tak, že parametr nextnevoláte. Obvykle můžete provádět akce před delegátem next i po něm, jak ukazuje následující příklad:

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();

Zkratování kanálu žádosti

Pokud delegát nepředá požadavek dalšímu delegátovi, označuje se to jako zkratování kanálu požadavků. Zkratování je často žádoucí, protože zabraňuje zbytečné práci. Middleware se statickými soubory může například fungovat jako terminální middleware tím, že zpracuje požadavek pro statický soubor a zbytek kanálu zkratuje. Middleware přidaný do kanálu před middleware, který ukončí další zpracování, stále zpracovává kód po příslušných příkazech next.Invoke. Ohledně pokusu o zápis do již odeslané odpovědi si však projděte následující upozornění.

Upozorňující

Nevolejte next.Invoke během odpovědi nebo po odeslání odpovědi klientovi. HttpResponse Po spuštění změny způsobí výjimku. Například nastavení hlaviček a stavového kódu vyvolá výjimku po spuštění odpovědi. Zápis do textu odpovědi po volání next:

  • Může způsobit porušení protokolu, například zápis více, než je uvedeno Content-Length.
  • Může poškodit formát těla, například zápis zápatí HTML do souboru CSS.

HasStarted je užitečná nápověda indikující, jestli byly odeslány hlavičky nebo zapisováno do textu.

Další informace najdete v tématu Middleware s krátkým okruhem po směrování.

Run Delegáti

Delegáti Run neobdrží parametr next. První delegát Run je vždy terminální a ukončí kanál. Použití Run je obvyklá konvence. Některé komponenty middlewaru můžou zpřístupnit metody Run[Middleware], které běží na konci kanálu:

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();

Pokud chcete zobrazit komentáře ke kódu přeložené do jiných jazyků, než je angličtina, dejte nám vědět v této diskuzi na GitHubu.

V předchozím příkladu delegát Run zapíše "Hello from 2nd delegate." do odpovědi a pak ukončí kanál. Pokud je za delegáta Run přidán jiný delegát Use nebo Run, nevolá se.

Preference přetížení app.Use, které vyžaduje předání kontextu dál

Rozšiřující metoda app.Use bez přidělování:

  • Vyžaduje předání kontextu do next.
  • Uloží dvě interní přidělení jednotlivých požadavků, které jsou vyžadovány při použití druhého přetížení.

Další informace najdete u tohoto problému na GitHubu.

Pořadí middlewaru

Následující diagram znázorňuje kompletní kanál zpracování požadavků pro aplikace ASP.NET Core MVC a Razor Pages. Můžete si prohlédnout, jak je v typické aplikaci uspořádané stávající middlewary seřazené a kde se přidávají vlastní middlewary. Máte plnou kontrolu nad tím, jak změnit pořadí stávajících middlewarů nebo vložit nové vlastní middlewary podle potřeby vašich scénářů.

Kanál middlewaru ASP.NET Core

Middleware Endpoint v předchozím diagramu spustí kanál filtru pro odpovídající typ aplikace – MVC nebo Razor Pages.

Middleware Routing v předchozím diagramu je zobrazený za Static Files. Toto je pořadí, které šablony projektů implementují explicitním voláním app.UseRouting. Pokud nezavoláte app.UseRouting, ve výchozím nastavení se middleware Routing spustí na začátku kanálu. Další informace najdete v tématu Směrování.

Kanál filtru ASP.NET Core

Pořadí, ve kterém jsou komponenty middlewaru přidány do souboru Program.cs, definuje pořadí, ve kterém se komponenty middlewaru vyvolávají pro požadavky, a obrácené pořadí pro odpovědi. Toto pořadí má zásadní důležitost pro zabezpečení, výkon a funkce.

Následující zvýrazněný kód v Program.cs přidává komponenty middlewaru související se zabezpečením v typickém doporučeném pořadí:

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();

V předchozím kódu:

  • Middleware, který se nepřidá při vytváření nové webové aplikace s účty jednotlivých uživatelů, je zakomentovaný.
  • Ne každý middleware se objevuje přesně v tomto pořadí, ale mnoho z nich ano. Příklad:
    • UseCors, UseAuthentication a UseAuthorization musí být v uvedeném pořadí.
    • UseCors musí aktuálně být před UseResponseCaching. Tento požadavek je vysvětlený v problému s dotnet/aspnetcore #23218 na GitHubu.
    • UseRequestLocalization musí se zobrazit před jakýmkoli middlewarem, který může zkontrolovat jazykovou verzi požadavku, app.UseStaticFiles()například .
    • UseRateLimiter musí být volána po UseRouting použití rozhraní API pro omezení rychlosti pro konkrétní koncové body. Pokud je například [EnableRateLimiting] použit atribut, UseRateLimiter musí být volána za UseRouting. Při volání pouze globálních limiterů UseRateLimiter lze volat před UseRouting.

V některých scénářích má middleware jiné řazení. Například pořadí ukládání do mezipaměti a komprese je specifické pro daný scénář a existuje několik platných pořadí. Příklad:

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

V předchozím kódu by bylo možné snížit využití procesoru uložením komprimované odpovědi do mezipaměti, ale mohlo by se stát, že budete ukládat do mezipaměti více reprezentací prostředku pomocí různých kompresních algoritmů, jako jsou například Gzip nebo Brotli.

Následující pořadí kombinuje statické soubory, aby se umožnilo ukládání komprimovaných statických souborů do mezipaměti:

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

Následující kód Program.cs přidá komponenty middlewaru pro běžné scénáře aplikací:

  1. Zpracování výjimek nebo chyb
    • Když se aplikace spustí ve vývojovém prostředí:
    • Když se aplikace spustí v produkčním prostředí:
      • Middleware Exception Handler (UseExceptionHandler) zachycuje výjimky vyvolané v následujících middlewarech.
      • Middleware HSTS (HTTP Strict Transport Security Protocol) (UseHsts) přidává hlavičku Strict-Transport-Security.
  2. Middleware HTTPS Redirection (UseHttpsRedirection) přesměruje požadavky HTTP na HTTPS.
  3. Middleware Static File (UseStaticFiles) vrací statické soubory a zkratuje další zpracování požadavků.
  4. Middleware Cookie Policy (UseCookiePolicy) zajišťuje soulad aplikace s Obecným nařízením EU o ochraně osobních údajů (GDPR).
  5. Middleware Routing (UseRouting) slouží ke směrování požadavků.
  6. Middleware Authentication (UseAuthentication) se pokusí ověřit uživatele předtím, než se mu povolí přístup k zabezpečeným prostředkům.
  7. Middleware Authorization (UseAuthorization) autorizuje uživatele pro přístup k zabezpečeným prostředkům.
  8. Middleware Session (UseSession) nastavuje a udržuje stav relace. Pokud aplikace používá stav relace, volejte middleware Session za middlewarem Cookie Policy a před middlewarem MVC.
  9. Middleware Endpoint Routing (UseEndpoints s MapRazorPages) slouží pro přidání koncových bodů Razor Pages do kanálu požadavků.
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();

V předchozím ukázkovém kódu je každá rozšiřující metoda middlewaru zpřístupněná v WebApplicationBuilder prostřednictvím oboru názvů Microsoft.AspNetCore.Builder.

UseExceptionHandler je první komponenta middlewaru přidaná do kanálu. Proto middleware Exception Handler zachycuje všechny výjimky, ke kterým dochází v pozdějších voláních.

Middleware Static File se volá v rané fázi zpracování kanálu, aby mohl zpracovávat požadavky a provést zkratování, aniž by se musely projít zbývající komponenty. Middleware Static File neposkytuje žádné kontroly autorizace. Všechny soubory obsluhované middlewarem Static File, včetně souborů pod wwwroot, jsou veřejně dostupné. Informace o přístupu k zabezpečení statických souborů najdete v tématu Statické soubory v ASP.NET Core.

Pokud požadavek nezpracuje middleware Static File, předá se do middlewaru Authentication (UseAuthentication), který provádí ověřování. Ověřování nezpůsobí zkratování neověřených požadavků. I když middleware Authentication ověřuje požadavky, autorizace (a zamítnutí) nastane až poté, co MVC vybere konkrétní kontroler MVC nebo Razor Pages a akci.

Následující příklad ukazuje pořadí middlewaru, ve kterém požadavky pro statické soubory zpracovává middleware Static File před middlewarem Response Compression. Statické soubory nejsou při tomto uspořádání middlewaru komprimovány. Odpovědi Razor Pages je možné komprimovat.

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

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

Informace o jednostrákových aplikacích najdete v tématu Přehled jednostrákových aplikací (SPA) v ASP.NET Core.

Pořadí UseCors a UseStaticFiles

Pořadí volání UseCors a UseStaticFiles závisí na aplikaci. Další informace najdete v tématu Pořadí UseCors a UseStaticFiles

Pořadí middlewaru Forwarded Headers

Middleware Forwarded Headers by měl běžet před ostatním middlewarem. Toto řazení zajišťuje, že middleware, který spoléhá na informace předávaných hlaviček, může využívat hodnoty hlaviček ke zpracování. Pokud chcete middleware Forwarded Headers spustit po diagnostice a middlewaru pro zpracování chyb, přečtěte si téma Middleware Ordered Headers.

Větvení kanálu middlewaru

Rozšíření Map se používají jako konvence pro větvení kanálu. Map rozvětví kanál požadavku na základě shod dané cesty k požadavku. Pokud cesta k požadavku začíná danou cestou, větev se spustí.

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

Následující tabulka ukazuje požadavky a odpovědi z http://localhost:1234 s využitím předchozího kódu.

Požádat Response
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.

Při použití Map se odpovídající segmenty cest odeberou z HttpRequest.Path a připojí ke HttpRequest.PathBase pro každý požadavek.

Map podporuje vnoření, například:

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map také může odpovídat více segmentům najednou:

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 rozvětví kanál požadavku na základě výsledku daného predikátu. K mapování požadavků na novou větev kanálu je možné použít jakýkoli predikát typu Func<HttpContext, bool>. V následujícím příkladu se predikát používá ke zjištění přítomnosti proměnné řetězce dotazu 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}");
    });
}

Následující tabulka ukazuje požadavky a odpovědi z http://localhost:1234 s využitím předchozího kódu:

Požádat Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

UseWhen také rozvětví kanál požadavku na základě výsledku daného predikátu. Na rozdíl od MapWhen se tato větev znovu připojí k hlavnímu kanálu, pokud nezpůsobí zkratování nebo neobsahuje terminální middleware:

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

V předchozím příkladu se pro všechny požadavky zapíše odpověď Hello from non-Map delegate.. Pokud požadavek obsahuje proměnnou řetězce dotazu branch, před opětovným připojením k hlavnímu kanálu se zaprotokoluje její hodnota.

Integrovaný middleware

ASP.NET Core se dodává s následujícími komponentami middlewaru. Sloupec Pořadí obsahuje poznámky k umístění middlewaru v kanálu zpracování požadavků a to, za jakých podmínek může middleware ukončit zpracování požadavků. Když middleware zkratuje kanál zpracování požadavků a zabrání dalšímu navazujícímu middlewaru zpracovat požadavek, označuje se jako terminální middleware. Další informace o zkratování najdete v části Vytvoření middlewarového kanálu s webapplication .

Middleware Popis Objednávka
Authentication Poskytuje podporu ověřování. Než je potřeba HttpContext.User. Terminál pro zpětné volání OAuth.
Autorizace Poskytuje podporu autorizace. Okamžitě po middlewaru Authentication.
Cookie Policy Sleduje souhlas uživatelů s ukládáním osobních údajů a vynucuje minimální standardy pro pole cookie, například secure a SameSite. Před middlewarem, který vydává soubory cookie. Příklady: Authentication, Session, MVC (TempData).
CORS Konfiguruje sdílení prostředků mezi zdroji (CORS). Před komponentami, které používají CORS. UseCors v současné době musí předcházet před UseResponseCaching, a to kvůli této chybě.
DeveloperExceptionPage Vygeneruje stránku s informacemi o chybách, které jsou určené jenom pro použití ve vývojovém prostředí. Před komponentami, které generují chyby. V případě vývojového prostředí šablony projektů automaticky zaregistrují tento middleware jako první middleware v kanálu.
Diagnostika Několik samostatných middlewarů, které poskytují stránku výjimek pro vývojáře, zpracování výjimek, stránky stavových kódů a výchozí webovou stránku pro nové aplikace. Před komponentami, které generují chyby. Terminál pro výjimky nebo obsluhu výchozí webové stránky pro nové aplikace.
Forwarded Headers Předá přeposílané hlavičky do aktuálního požadavku. Před komponentami, které využívají aktualizovaná pole. Příklady: Schéma, hostitel, IP adresa klienta, metoda.
Health Check Kontroluje stav aplikace ASP.NET Core a jejích závislostí, jako je kontrola dostupnosti databáze. Terminál, pokud požadavek odpovídá koncovému bodu kontroly stavu.
Header Propagation Šíří hlavičky HTTP z příchozího požadavku do odchozích požadavků klienta HTTP.
Protokolování HTTP Protokoluje požadavky HTTP a odpovědi. Na začátku kanálu middlewaru.
HTTP Method Override Umožňuje, aby příchozí požadavek POST přepsal metodu. Před komponentami, které využívají aktualizovanou metodu.
HTTPS Redirection Přesměruje všechny požadavky HTTP na HTTPS. Před komponentami, které využívají danou adresu URL.
HTTP Strict Transport Security (HSTS) Middleware pro vylepšení zabezpečení, který přidává speciální hlavičku odpovědi. Před odesláním odpovědí a po komponentách, které upravují požadavky. Příklady: Přeposílané hlavičky, přepsání adresy URL.
MVC Zpracovává požadavky s využitím MVC / Razor Pages. Terminál, pokud požadavek odpovídá trase.
OWIN Spolupracuje s aplikacemi, servery a middlewarem založenými na OWIN. Terminál, pokud middleware OWIN plně zpracuje daný požadavek.
Výstupní Ukládání do mezipaměti Poskytuje podporu pro ukládání odpovědí do mezipaměti na základě konfigurace. Před komponentami, které vyžadují ukládání do mezipaměti. Je potřeba použít UseRouting před UseOutputCaching. Je potřeba použít UseCORS před UseOutputCaching.
Ukládání odpovědí do mezipaměti Poskytuje podporu pro ukládání odpovědí do mezipaměti. To vyžaduje, aby klientská účast fungovala. K úplnému řízení serveru použijte ukládání výstupu do mezipaměti. Před komponentami, které vyžadují ukládání do mezipaměti. Je potřeba použít UseCORS před UseResponseCaching. Aplikace uživatelského rozhraní, jako Razor jsou stránky, obvykle nejsou užitečné, protože prohlížeče obvykle nastavují hlavičky požadavků, které brání ukládání do mezipaměti. Ukládání výstupu do mezipaměti přináší výhody aplikací uživatelského rozhraní.
Request Decompression Poskytuje podporu pro dekompresi požadavků. Před komponentami, které čtou text požadavku.
Response Compression Poskytuje podporu pro kompresi odpovědí. Před komponentami, které vyžadují kompresi.
Request Localization Poskytuje podporu lokalizace. Před komponentami citlivými na lokalizaci. Při použití RouteDataRequestCultureProvider musí následovat po middlewaru Routing.
Časové limity požadavků Poskytuje podporu pro konfiguraci časových limitů požadavků, globálního a koncového bodu. UseRequestTimeouts musí přijít po UseExceptionHandler, UseDeveloperExceptionPagea UseRouting.
Endpoint Routing Definuje a omezuje trasy požadavků. Terminál pro vyhovující trasy.
SPA Zpracovává všechny požadavky od tohoto bodu v řetězci middlewaru vrácením výchozí stránky pro jednostránkovou aplikaci (SPA). U konce řetězce, aby měl přednost ostatní middleware pro obsluhu statických souborů, akcí MVC atd.
Relace Poskytuje podporu pro správu uživatelských relací. Před komponentami, které vyžadují middleware Session.
Static Files Poskytuje podporu pro obsluhu statických souborů a procházení adresářů. Terminál, pokud požadavek odpovídá souboru.
Přepsání adresy URL Poskytuje podporu pro přepis adres URL a přesměrování požadavků. Před komponentami, které využívají danou adresu URL.
W3CLogging Generuje protokoly přístupu k serveru v rozšířeném formátu souborů protokolu W3C. Na začátku kanálu middlewaru.
Webové sokety Povoluje protokol WebSocket. Před komponentami, které jsou potřeba k přijetí požadavků WebSocket.

Další materiály

Autor: Rick Anderson a Steve Smith

Middleware je software sestavený do kanálu aplikace pro zpracování požadavků a odpovědí. Každá komponenta:

  • Určuje, jestli se má požadavek předat další komponentě v kanálu.
  • Může provádět práci před a za další komponentou v kanálu.

K sestavení kanálu požadavků se používají delegáti požadavků. Delegáti požadavků zpracovávají každý požadavek HTTP.

Delegáti požadavků se konfigurují pomocí rozšiřujících metod Run, Map a Use. Jednotlivý delegát požadavku může být zadán na řádku jako anonymní metoda (tzv. in-line middleware) nebo může být definován v opakovaně použitelné třídě. Tyto opakovaně použitelné třídy a vložené anonymní metody jsou middleware, označované také jako komponenty middlewaru. Každá komponenta middlewaru v kanálu požadavků zodpovídá za vyvolání další komponenty v kanálu nebo zkratování kanálu. Když middleware zkratuje, označuje se jako terminální middleware, protože brání dalšímu middlewaru ve zpracování požadavku.

Téma věnované migraci obslužných rutin HTTP a modulů do middlewaru ASP.NET Core vysvětluje rozdíl mezi kanály požadavků v prostředích ASP.NET Core a ASP.NET 4.x a poskytuje další ukázky middlewaru.

Analýza kódu middlewaru

ASP.NET Core obsahuje celou řadu analyzátorů Compile Platform, které kontrolují kód aplikace z hlediska kvality. Další informace najdete v tématu Analýza kódu v aplikacích ASP.NET Core.

Vytvoření kanálu middlewaru s využitím WebApplication

Kanál požadavků ASP.NET Core se skládá z posloupnosti delegátů požadavků, kteří se volají jeden po druhém. Tento koncept demonstruje následující schéma. Vlákno spouštění postupuje podle černých šipek.

Vzor zpracování požadavku zobrazující příchozí požadavek, jeho zpracování třemi middlewary a odpověď opouštějící aplikaci. Každý middleware spustí svou logiku a předá požadavek dalšímu middlewaru v příkazu next(). Poté, co třetí middleware zpracuje požadavek, projde požadavek zpět přes předchozí dva middlewary v opačném pořadí k dalšímu zpracování po příkazech next() a poté opustí aplikaci jako odpověď klientovi.

Každý delegát může provádět operace před a za dalším delegátem. Delegáti zpracování výjimek by se měli volat v rané fázi zpracování kanálu, aby mohli zachytit výjimky, ke kterým dochází v pozdějších fázích zpracování kanálu.

Nejjednodušší možná aplikace ASP.NET Core nastavuje jednoho delegáta požadavků, který zpracovává všechny požadavky. Tento případ nezahrnuje vlastní kanál požadavků. Místo toho se jako reakce na každý požadavek HTTP volá jedna anonymní funkce.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Run();

Zřetězte několik delegátů požadavků společně s Use. Parametr next představuje dalšího delegáta v kanálu. Kanál můžete zkratovat tak, že parametr nextnevoláte. Obvykle můžete provádět akce před delegátem next i po něm, jak ukazuje následující příklad:

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();

Pokud delegát nepředá požadavek dalšímu delegátovi, označuje se to jako zkratování kanálu požadavků. Zkratování je často žádoucí, protože zabraňuje zbytečné práci. Middleware se statickými soubory může například fungovat jako terminální middleware tím, že zpracuje požadavek pro statický soubor a zbytek kanálu zkratuje. Middleware přidaný do kanálu před middleware, který ukončí další zpracování, stále zpracovává kód po příslušných příkazech next.Invoke. Ohledně pokusu o zápis do již odeslané odpovědi si však projděte následující upozornění.

Upozorňující

Po odeslání odpovědi klientovi nevolejte next.Invoke. Změny v HttpResponse po spuštění odpovědi vyvolají výjimku. Například nastavení hlaviček a stavového kódu vyvolá výjimku. Zápis do textu odpovědi po volání next:

  • Může způsobit porušení protokolu. Příklad: Delší zápis, než je stanovená délka Content-Length.
  • Může poškodit formát textu. Příklad: Zápis zápatí HTML do souboru CSS.

HasStarted je užitečná nápověda indikující, jestli byly odeslány hlavičky nebo zapisováno do textu.

Delegáti Run neobdrží parametr next. První delegát Run je vždy terminální a ukončí kanál. Použití Run je obvyklá konvence. Některé komponenty middlewaru můžou zpřístupnit metody Run[Middleware], které běží na konci kanálu:

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();

Pokud chcete zobrazit komentáře ke kódu přeložené do jiných jazyků, než je angličtina, dejte nám vědět v této diskuzi na GitHubu.

V předchozím příkladu delegát Run zapíše "Hello from 2nd delegate." do odpovědi a pak ukončí kanál. Pokud je za delegáta Run přidán jiný delegát Use nebo Run, nevolá se.

Preference přetížení app.Use, které vyžaduje předání kontextu dál

Rozšiřující metoda app.Use bez přidělování:

  • Vyžaduje předání kontextu do next.
  • Uloží dvě interní přidělení jednotlivých požadavků, které jsou vyžadovány při použití druhého přetížení.

Další informace najdete u tohoto problému na GitHubu.

Pořadí middlewaru

Následující diagram znázorňuje kompletní kanál zpracování požadavků pro aplikace ASP.NET Core MVC a Razor Pages. Můžete si prohlédnout, jak je v typické aplikaci uspořádané stávající middlewary seřazené a kde se přidávají vlastní middlewary. Máte plnou kontrolu nad tím, jak změnit pořadí stávajících middlewarů nebo vložit nové vlastní middlewary podle potřeby vašich scénářů.

Kanál middlewaru ASP.NET Core

Middleware Endpoint v předchozím diagramu spustí kanál filtru pro odpovídající typ aplikace – MVC nebo Razor Pages.

Middleware Routing v předchozím diagramu je zobrazený za Static Files. Toto je pořadí, které šablony projektů implementují explicitním voláním app.UseRouting. Pokud nezavoláte app.UseRouting, ve výchozím nastavení se middleware Routing spustí na začátku kanálu. Další informace najdete v tématu Směrování.

Kanál filtru ASP.NET Core

Pořadí, ve kterém jsou komponenty middlewaru přidány do souboru Program.cs, definuje pořadí, ve kterém se komponenty middlewaru vyvolávají pro požadavky, a obrácené pořadí pro odpovědi. Toto pořadí má zásadní důležitost pro zabezpečení, výkon a funkce.

Následující zvýrazněný kód v Program.cs přidává komponenty middlewaru související se zabezpečením v typickém doporučeném pořadí:

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();

V předchozím kódu:

  • Middleware, který se nepřidá při vytváření nové webové aplikace s účty jednotlivých uživatelů, je zakomentovaný.
  • Ne každý middleware se objevuje přesně v tomto pořadí, ale mnoho z nich ano. Příklad:
    • UseCors, UseAuthentication a UseAuthorization musí být v uvedeném pořadí.
    • UseCors musí aktuálně být před UseResponseCaching. Tento požadavek je vysvětlený v problému s dotnet/aspnetcore #23218 na GitHubu.
    • UseRequestLocalization musí se zobrazit před jakýmkoli middlewarem, který může zkontrolovat jazykovou verzi požadavku, app.UseStaticFiles()například .
    • UseRateLimiter musí být volána po UseRouting použití rozhraní API pro omezení rychlosti pro konkrétní koncové body. Pokud je například [EnableRateLimiting] použit atribut, UseRateLimiter musí být volána za UseRouting. Při volání pouze globálních limiterů UseRateLimiter lze volat před UseRouting.

V některých scénářích má middleware jiné řazení. Například pořadí ukládání do mezipaměti a komprese je specifické pro daný scénář a existuje několik platných pořadí. Příklad:

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

V předchozím kódu by bylo možné snížit využití procesoru uložením komprimované odpovědi do mezipaměti, ale mohlo by se stát, že budete ukládat do mezipaměti více reprezentací prostředku pomocí různých kompresních algoritmů, jako jsou například Gzip nebo Brotli.

Následující pořadí kombinuje statické soubory, aby se umožnilo ukládání komprimovaných statických souborů do mezipaměti:

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

Následující kód Program.cs přidá komponenty middlewaru pro běžné scénáře aplikací:

  1. Zpracování výjimek nebo chyb
    • Když se aplikace spustí ve vývojovém prostředí:
    • Když se aplikace spustí v produkčním prostředí:
      • Middleware Exception Handler (UseExceptionHandler) zachycuje výjimky vyvolané v následujících middlewarech.
      • Middleware HSTS (HTTP Strict Transport Security Protocol) (UseHsts) přidává hlavičku Strict-Transport-Security.
  2. Middleware HTTPS Redirection (UseHttpsRedirection) přesměruje požadavky HTTP na HTTPS.
  3. Middleware Static File (UseStaticFiles) vrací statické soubory a zkratuje další zpracování požadavků.
  4. Middleware Cookie Policy (UseCookiePolicy) zajišťuje soulad aplikace s Obecným nařízením EU o ochraně osobních údajů (GDPR).
  5. Middleware Routing (UseRouting) slouží ke směrování požadavků.
  6. Middleware Authentication (UseAuthentication) se pokusí ověřit uživatele předtím, než se mu povolí přístup k zabezpečeným prostředkům.
  7. Middleware Authorization (UseAuthorization) autorizuje uživatele pro přístup k zabezpečeným prostředkům.
  8. Middleware Session (UseSession) nastavuje a udržuje stav relace. Pokud aplikace používá stav relace, volejte middleware Session za middlewarem Cookie Policy a před middlewarem MVC.
  9. Middleware Endpoint Routing (UseEndpoints s MapRazorPages) slouží pro přidání koncových bodů Razor Pages do kanálu požadavků.
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();

V předchozím ukázkovém kódu je každá rozšiřující metoda middlewaru zpřístupněná v WebApplicationBuilder prostřednictvím oboru názvů Microsoft.AspNetCore.Builder.

UseExceptionHandler je první komponenta middlewaru přidaná do kanálu. Proto middleware Exception Handler zachycuje všechny výjimky, ke kterým dochází v pozdějších voláních.

Middleware Static File se volá v rané fázi zpracování kanálu, aby mohl zpracovávat požadavky a provést zkratování, aniž by se musely projít zbývající komponenty. Middleware Static File neposkytuje žádné kontroly autorizace. Všechny soubory obsluhované middlewarem Static File, včetně souborů pod wwwroot, jsou veřejně dostupné. Informace o přístupu k zabezpečení statických souborů najdete v tématu Statické soubory v ASP.NET Core.

Pokud požadavek nezpracuje middleware Static File, předá se do middlewaru Authentication (UseAuthentication), který provádí ověřování. Ověřování nezpůsobí zkratování neověřených požadavků. I když middleware Authentication ověřuje požadavky, autorizace (a zamítnutí) nastane až poté, co MVC vybere konkrétní kontroler MVC nebo Razor Pages a akci.

Následující příklad ukazuje pořadí middlewaru, ve kterém požadavky pro statické soubory zpracovává middleware Static File před middlewarem Response Compression. Statické soubory nejsou při tomto uspořádání middlewaru komprimovány. Odpovědi Razor Pages je možné komprimovat.

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

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

Informace o jednostránkových aplikacích najdete v průvodcích pro šablony projektů React a Angular.

Pořadí UseCors a UseStaticFiles

Pořadí volání UseCors a UseStaticFiles závisí na aplikaci. Další informace najdete v tématu Pořadí UseCors a UseStaticFiles

Pořadí middlewaru Forwarded Headers

Middleware Forwarded Headers by měl běžet před ostatním middlewarem. Toto řazení zajišťuje, že middleware, který spoléhá na informace předávaných hlaviček, může využívat hodnoty hlaviček ke zpracování. Pokud chcete middleware Forwarded Headers spustit po diagnostice a middlewaru pro zpracování chyb, přečtěte si téma Middleware Ordered Headers.

Větvení kanálu middlewaru

Rozšíření Map se používají jako konvence pro větvení kanálu. Map rozvětví kanál požadavku na základě shod dané cesty k požadavku. Pokud cesta k požadavku začíná danou cestou, větev se spustí.

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

Následující tabulka ukazuje požadavky a odpovědi z http://localhost:1234 s využitím předchozího kódu.

Požádat Response
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.

Při použití Map se odpovídající segmenty cest odeberou z HttpRequest.Path a připojí ke HttpRequest.PathBase pro každý požadavek.

Map podporuje vnoření, například:

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map také může odpovídat více segmentům najednou:

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 rozvětví kanál požadavku na základě výsledku daného predikátu. K mapování požadavků na novou větev kanálu je možné použít jakýkoli predikát typu Func<HttpContext, bool>. V následujícím příkladu se predikát používá ke zjištění přítomnosti proměnné řetězce dotazu 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}");
    });
}

Následující tabulka ukazuje požadavky a odpovědi z http://localhost:1234 s využitím předchozího kódu:

Požádat Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

UseWhen také rozvětví kanál požadavku na základě výsledku daného predikátu. Na rozdíl od MapWhen se tato větev znovu připojí k hlavnímu kanálu, pokud nezpůsobí zkratování nebo neobsahuje terminální middleware:

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

V předchozím příkladu se pro všechny požadavky zapíše odpověď Hello from non-Map delegate.. Pokud požadavek obsahuje proměnnou řetězce dotazu branch, před opětovným připojením k hlavnímu kanálu se zaprotokoluje její hodnota.

Integrovaný middleware

ASP.NET Core se dodává s následujícími komponentami middlewaru. Sloupec Pořadí obsahuje poznámky k umístění middlewaru v kanálu zpracování požadavků a to, za jakých podmínek může middleware ukončit zpracování požadavků. Když middleware zkratuje kanál zpracování požadavků a zabrání dalšímu navazujícímu middlewaru zpracovat požadavek, označuje se jako terminální middleware. Další informace o zkratování najdete v části Vytvoření middlewarového kanálu s webapplication .

Middleware Popis Objednávka
Authentication Poskytuje podporu ověřování. Než je potřeba HttpContext.User. Terminál pro zpětné volání OAuth.
Autorizace Poskytuje podporu autorizace. Okamžitě po middlewaru Authentication.
Cookie Policy Sleduje souhlas uživatelů s ukládáním osobních údajů a vynucuje minimální standardy pro pole cookie, například secure a SameSite. Před middlewarem, který vydává soubory cookie. Příklady: Authentication, Session, MVC (TempData).
CORS Konfiguruje sdílení prostředků mezi zdroji (CORS). Před komponentami, které používají CORS. UseCors v současné době musí předcházet před UseResponseCaching, a to kvůli této chybě.
DeveloperExceptionPage Vygeneruje stránku s informacemi o chybách, které jsou určené jenom pro použití ve vývojovém prostředí. Před komponentami, které generují chyby. V případě vývojového prostředí šablony projektů automaticky zaregistrují tento middleware jako první middleware v kanálu.
Diagnostika Několik samostatných middlewarů, které poskytují stránku výjimek pro vývojáře, zpracování výjimek, stránky stavových kódů a výchozí webovou stránku pro nové aplikace. Před komponentami, které generují chyby. Terminál pro výjimky nebo obsluhu výchozí webové stránky pro nové aplikace.
Forwarded Headers Předá přeposílané hlavičky do aktuálního požadavku. Před komponentami, které využívají aktualizovaná pole. Příklady: Schéma, hostitel, IP adresa klienta, metoda.
Health Check Kontroluje stav aplikace ASP.NET Core a jejích závislostí, jako je kontrola dostupnosti databáze. Terminál, pokud požadavek odpovídá koncovému bodu kontroly stavu.
Header Propagation Šíří hlavičky HTTP z příchozího požadavku do odchozích požadavků klienta HTTP.
Protokolování HTTP Protokoluje požadavky HTTP a odpovědi. Na začátku kanálu middlewaru.
HTTP Method Override Umožňuje, aby příchozí požadavek POST přepsal metodu. Před komponentami, které využívají aktualizovanou metodu.
HTTPS Redirection Přesměruje všechny požadavky HTTP na HTTPS. Před komponentami, které využívají danou adresu URL.
HTTP Strict Transport Security (HSTS) Middleware pro vylepšení zabezpečení, který přidává speciální hlavičku odpovědi. Před odesláním odpovědí a po komponentách, které upravují požadavky. Příklady: Přeposílané hlavičky, přepsání adresy URL.
MVC Zpracovává požadavky s využitím MVC / Razor Pages. Terminál, pokud požadavek odpovídá trase.
OWIN Spolupracuje s aplikacemi, servery a middlewarem založenými na OWIN. Terminál, pokud middleware OWIN plně zpracuje daný požadavek.
Výstupní Ukládání do mezipaměti Poskytuje podporu pro ukládání odpovědí do mezipaměti na základě konfigurace. Před komponentami, které vyžadují ukládání do mezipaměti. Je potřeba použít UseRouting před UseOutputCaching. Je potřeba použít UseCORS před UseOutputCaching.
Ukládání odpovědí do mezipaměti Poskytuje podporu pro ukládání odpovědí do mezipaměti. To vyžaduje, aby klientská účast fungovala. K úplnému řízení serveru použijte ukládání výstupu do mezipaměti. Před komponentami, které vyžadují ukládání do mezipaměti. Je potřeba použít UseCORS před UseResponseCaching. Aplikace uživatelského rozhraní, jako Razor jsou stránky, obvykle nejsou užitečné, protože prohlížeče obvykle nastavují hlavičky požadavků, které brání ukládání do mezipaměti. Ukládání výstupu do mezipaměti přináší výhody aplikací uživatelského rozhraní.
Request Decompression Poskytuje podporu pro dekompresi požadavků. Před komponentami, které čtou text požadavku.
Response Compression Poskytuje podporu pro kompresi odpovědí. Před komponentami, které vyžadují kompresi.
Request Localization Poskytuje podporu lokalizace. Před komponentami citlivými na lokalizaci. Při použití RouteDataRequestCultureProvider musí následovat po middlewaru Routing.
Endpoint Routing Definuje a omezuje trasy požadavků. Terminál pro vyhovující trasy.
SPA Zpracovává všechny požadavky od tohoto bodu v řetězci middlewaru vrácením výchozí stránky pro jednostránkovou aplikaci (SPA). U konce řetězce, aby měl přednost ostatní middleware pro obsluhu statických souborů, akcí MVC atd.
Relace Poskytuje podporu pro správu uživatelských relací. Před komponentami, které vyžadují middleware Session.
Static Files Poskytuje podporu pro obsluhu statických souborů a procházení adresářů. Terminál, pokud požadavek odpovídá souboru.
Přepsání adresy URL Poskytuje podporu pro přepis adres URL a přesměrování požadavků. Před komponentami, které využívají danou adresu URL.
W3CLogging Generuje protokoly přístupu k serveru v rozšířeném formátu souborů protokolu W3C. Na začátku kanálu middlewaru.
Webové sokety Povoluje protokol WebSocket. Před komponentami, které jsou potřeba k přijetí požadavků WebSocket.

Další materiály

Autor: Rick Anderson a Steve Smith

Middleware je software sestavený do kanálu aplikace pro zpracování požadavků a odpovědí. Každá komponenta:

  • Určuje, jestli se má požadavek předat další komponentě v kanálu.
  • Může provádět práci před a za další komponentou v kanálu.

K sestavení kanálu požadavků se používají delegáti požadavků. Delegáti požadavků zpracovávají každý požadavek HTTP.

Delegáti požadavků se konfigurují pomocí rozšiřujících metod Run, Map a Use. Jednotlivý delegát požadavku může být zadán na řádku jako anonymní metoda (tzv. in-line middleware) nebo může být definován v opakovaně použitelné třídě. Tyto opakovaně použitelné třídy a vložené anonymní metody jsou middleware, označované také jako komponenty middlewaru. Každá komponenta middlewaru v kanálu požadavků zodpovídá za vyvolání další komponenty v kanálu nebo zkratování kanálu. Když middleware zkratuje, označuje se jako terminální middleware, protože brání dalšímu middlewaru ve zpracování požadavku.

Téma věnované migraci obslužných rutin HTTP a modulů do middlewaru ASP.NET Core vysvětluje rozdíl mezi kanály požadavků v prostředích ASP.NET Core a ASP.NET 4.x a poskytuje další ukázky middlewaru.

Analýza kódu middlewaru

ASP.NET Core obsahuje celou řadu analyzátorů Compile Platform, které kontrolují kód aplikace z hlediska kvality. Další informace najdete v tématu Analýza kódu v aplikacích ASP.NET Core.

Vytvoření kanálu middlewaru s využitím WebApplication

Kanál požadavků ASP.NET Core se skládá z posloupnosti delegátů požadavků, kteří se volají jeden po druhém. Tento koncept demonstruje následující schéma. Vlákno spouštění postupuje podle černých šipek.

Vzor zpracování požadavku zobrazující příchozí požadavek, jeho zpracování třemi middlewary a odpověď opouštějící aplikaci. Každý middleware spustí svou logiku a předá požadavek dalšímu middlewaru v příkazu next(). Poté, co třetí middleware zpracuje požadavek, projde požadavek zpět přes předchozí dva middlewary v opačném pořadí k dalšímu zpracování po příkazech next() a poté opustí aplikaci jako odpověď klientovi.

Každý delegát může provádět operace před a za dalším delegátem. Delegáti zpracování výjimek by se měli volat v rané fázi zpracování kanálu, aby mohli zachytit výjimky, ke kterým dochází v pozdějších fázích zpracování kanálu.

Nejjednodušší možná aplikace ASP.NET Core nastavuje jednoho delegáta požadavků, který zpracovává všechny požadavky. Tento případ nezahrnuje vlastní kanál požadavků. Místo toho se jako reakce na každý požadavek HTTP volá jedna anonymní funkce.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Run();

Zřetězte několik delegátů požadavků společně s Use. Parametr next představuje dalšího delegáta v kanálu. Kanál můžete zkratovat tak, že parametr nextnevoláte. Obvykle můžete provádět akce před delegátem next i po něm, jak ukazuje následující příklad:

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();

Pokud delegát nepředá požadavek dalšímu delegátovi, označuje se to jako zkratování kanálu požadavků. Zkratování je často žádoucí, protože zabraňuje zbytečné práci. Middleware se statickými soubory může například fungovat jako terminální middleware tím, že zpracuje požadavek pro statický soubor a zbytek kanálu zkratuje. Middleware přidaný do kanálu před middleware, který ukončí další zpracování, stále zpracovává kód po příslušných příkazech next.Invoke. Ohledně pokusu o zápis do již odeslané odpovědi si však projděte následující upozornění.

Upozorňující

Po odeslání odpovědi klientovi nevolejte next.Invoke. Změny v HttpResponse po spuštění odpovědi vyvolají výjimku. Například nastavení hlaviček a stavového kódu vyvolá výjimku. Zápis do textu odpovědi po volání next:

  • Může způsobit porušení protokolu. Příklad: Delší zápis, než je stanovená délka Content-Length.
  • Může poškodit formát textu. Příklad: Zápis zápatí HTML do souboru CSS.

HasStarted je užitečná nápověda indikující, jestli byly odeslány hlavičky nebo zapisováno do textu.

Delegáti Run neobdrží parametr next. První delegát Run je vždy terminální a ukončí kanál. Použití Run je obvyklá konvence. Některé komponenty middlewaru můžou zpřístupnit metody Run[Middleware], které běží na konci kanálu:

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();

Pokud chcete zobrazit komentáře ke kódu přeložené do jiných jazyků, než je angličtina, dejte nám vědět v této diskuzi na GitHubu.

V předchozím příkladu delegát Run zapíše "Hello from 2nd delegate." do odpovědi a pak ukončí kanál. Pokud je za delegáta Run přidán jiný delegát Use nebo Run, nevolá se.

Preference přetížení app.Use, které vyžaduje předání kontextu dál

Rozšiřující metoda app.Use bez přidělování:

  • Vyžaduje předání kontextu do next.
  • Uloží dvě interní přidělení jednotlivých požadavků, které jsou vyžadovány při použití druhého přetížení.

Další informace najdete u tohoto problému na GitHubu.

Pořadí middlewaru

Následující diagram znázorňuje kompletní kanál zpracování požadavků pro aplikace ASP.NET Core MVC a Razor Pages. Můžete si prohlédnout, jak je v typické aplikaci uspořádané stávající middlewary seřazené a kde se přidávají vlastní middlewary. Máte plnou kontrolu nad tím, jak změnit pořadí stávajících middlewarů nebo vložit nové vlastní middlewary podle potřeby vašich scénářů.

Kanál middlewaru ASP.NET Core

Middleware Endpoint v předchozím diagramu spustí kanál filtru pro odpovídající typ aplikace – MVC nebo Razor Pages.

Middleware Routing v předchozím diagramu je zobrazený za Static Files. Toto je pořadí, které šablony projektů implementují explicitním voláním app.UseRouting. Pokud nezavoláte app.UseRouting, ve výchozím nastavení se middleware Routing spustí na začátku kanálu. Další informace najdete v tématu Směrování.

Kanál filtru ASP.NET Core

Pořadí, ve kterém jsou komponenty middlewaru přidány do souboru Program.cs, definuje pořadí, ve kterém se komponenty middlewaru vyvolávají pro požadavky, a obrácené pořadí pro odpovědi. Toto pořadí má zásadní důležitost pro zabezpečení, výkon a funkce.

Následující zvýrazněný kód v Program.cs přidává komponenty middlewaru související se zabezpečením v typickém doporučeném pořadí:

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();

V předchozím kódu:

  • Middleware, který se nepřidá při vytváření nové webové aplikace s účty jednotlivých uživatelů, je zakomentovaný.
  • Ne každý middleware se objevuje přesně v tomto pořadí, ale mnoho z nich ano. Příklad:
    • UseCors, UseAuthentication a UseAuthorization musí být v uvedeném pořadí.
    • UseCors musí aktuálně být před UseResponseCaching. Tento požadavek je vysvětlený v problému s dotnet/aspnetcore #23218 na GitHubu.
    • UseRequestLocalization musí být uvedený před jakýmkoli jiným middlewarem, který kontroluje kulturu požadavků (například app.UseMvcWithDefaultRoute()).

V některých scénářích má middleware jiné řazení. Například pořadí ukládání do mezipaměti a komprese je specifické pro daný scénář a existuje několik platných pořadí. Příklad:

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

V předchozím kódu by bylo možné snížit využití procesoru uložením komprimované odpovědi do mezipaměti, ale mohlo by se stát, že budete ukládat do mezipaměti více reprezentací prostředku pomocí různých kompresních algoritmů, jako jsou například Gzip nebo Brotli.

Následující pořadí kombinuje statické soubory, aby se umožnilo ukládání komprimovaných statických souborů do mezipaměti:

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

Následující kód Program.cs přidá komponenty middlewaru pro běžné scénáře aplikací:

  1. Zpracování výjimek nebo chyb
    • Když se aplikace spustí ve vývojovém prostředí:
    • Když se aplikace spustí v produkčním prostředí:
      • Middleware Exception Handler (UseExceptionHandler) zachycuje výjimky vyvolané v následujících middlewarech.
      • Middleware HSTS (HTTP Strict Transport Security Protocol) (UseHsts) přidává hlavičku Strict-Transport-Security.
  2. Middleware HTTPS Redirection (UseHttpsRedirection) přesměruje požadavky HTTP na HTTPS.
  3. Middleware Static File (UseStaticFiles) vrací statické soubory a zkratuje další zpracování požadavků.
  4. Middleware Cookie Policy (UseCookiePolicy) zajišťuje soulad aplikace s Obecným nařízením EU o ochraně osobních údajů (GDPR).
  5. Middleware Routing (UseRouting) slouží ke směrování požadavků.
  6. Middleware Authentication (UseAuthentication) se pokusí ověřit uživatele předtím, než se mu povolí přístup k zabezpečeným prostředkům.
  7. Middleware Authorization (UseAuthorization) autorizuje uživatele pro přístup k zabezpečeným prostředkům.
  8. Middleware Session (UseSession) nastavuje a udržuje stav relace. Pokud aplikace používá stav relace, volejte middleware Session za middlewarem Cookie Policy a před middlewarem MVC.
  9. Middleware Endpoint Routing (UseEndpoints s MapRazorPages) slouží pro přidání koncových bodů Razor Pages do kanálu požadavků.
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();

V předchozím ukázkovém kódu je každá rozšiřující metoda middlewaru zpřístupněná v WebApplicationBuilder prostřednictvím oboru názvů Microsoft.AspNetCore.Builder.

UseExceptionHandler je první komponenta middlewaru přidaná do kanálu. Proto middleware Exception Handler zachycuje všechny výjimky, ke kterým dochází v pozdějších voláních.

Middleware Static File se volá v rané fázi zpracování kanálu, aby mohl zpracovávat požadavky a provést zkratování, aniž by se musely projít zbývající komponenty. Middleware Static File neposkytuje žádné kontroly autorizace. Všechny soubory obsluhované middlewarem Static File, včetně souborů pod wwwroot, jsou veřejně dostupné. Informace o přístupu k zabezpečení statických souborů najdete v tématu Statické soubory v ASP.NET Core.

Pokud požadavek nezpracuje middleware Static File, předá se do middlewaru Authentication (UseAuthentication), který provádí ověřování. Ověřování nezpůsobí zkratování neověřených požadavků. I když middleware Authentication ověřuje požadavky, autorizace (a zamítnutí) nastane až poté, co MVC vybere konkrétní kontroler MVC nebo Razor Pages a akci.

Následující příklad ukazuje pořadí middlewaru, ve kterém požadavky pro statické soubory zpracovává middleware Static File před middlewarem Response Compression. Statické soubory nejsou při tomto uspořádání middlewaru komprimovány. Odpovědi Razor Pages je možné komprimovat.

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

app.UseRouting();

app.UseResponseCompression();

app.MapRazorPages();

Informace o jednostránkových aplikacích najdete v průvodcích pro šablony projektů React a Angular.

Pořadí UseCors a UseStaticFiles

Pořadí volání UseCors a UseStaticFiles závisí na aplikaci. Další informace najdete v tématu Pořadí UseCors a UseStaticFiles

Pořadí middlewaru Forwarded Headers

Middleware Forwarded Headers by měl běžet před ostatním middlewarem. Toto řazení zajišťuje, že middleware, který spoléhá na informace předávaných hlaviček, může využívat hodnoty hlaviček ke zpracování. Pokud chcete middleware Forwarded Headers spustit po diagnostice a middlewaru pro zpracování chyb, přečtěte si téma Middleware Ordered Headers.

Větvení kanálu middlewaru

Rozšíření Map se používají jako konvence pro větvení kanálu. Map rozvětví kanál požadavku na základě shod dané cesty k požadavku. Pokud cesta k požadavku začíná danou cestou, větev se spustí.

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

Následující tabulka ukazuje požadavky a odpovědi z http://localhost:1234 s využitím předchozího kódu.

Požádat Response
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.

Při použití Map se odpovídající segmenty cest odeberou z HttpRequest.Path a připojí ke HttpRequest.PathBase pro každý požadavek.

Map podporuje vnoření, například:

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map také může odpovídat více segmentům najednou:

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 rozvětví kanál požadavku na základě výsledku daného predikátu. K mapování požadavků na novou větev kanálu je možné použít jakýkoli predikát typu Func<HttpContext, bool>. V následujícím příkladu se predikát používá ke zjištění přítomnosti proměnné řetězce dotazu 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}");
    });
}

Následující tabulka ukazuje požadavky a odpovědi z http://localhost:1234 s využitím předchozího kódu:

Požádat Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

UseWhen také rozvětví kanál požadavku na základě výsledku daného predikátu. Na rozdíl od MapWhen se tato větev znovu připojí k hlavnímu kanálu, pokud nezpůsobí zkratování nebo neobsahuje terminální middleware:

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

V předchozím příkladu se pro všechny požadavky zapíše odpověď Hello from non-Map delegate.. Pokud požadavek obsahuje proměnnou řetězce dotazu branch, před opětovným připojením k hlavnímu kanálu se zaprotokoluje její hodnota.

Integrovaný middleware

ASP.NET Core se dodává s následujícími komponentami middlewaru. Sloupec Pořadí obsahuje poznámky k umístění middlewaru v kanálu zpracování požadavků a to, za jakých podmínek může middleware ukončit zpracování požadavků. Když middleware zkratuje kanál zpracování požadavků a zabrání dalšímu navazujícímu middlewaru zpracovat požadavek, označuje se jako terminální middleware. Další informace o zkratování najdete v části Vytvoření middlewarového kanálu s webapplication .

Middleware Popis Objednávka
Authentication Poskytuje podporu ověřování. Než je potřeba HttpContext.User. Terminál pro zpětné volání OAuth.
Autorizace Poskytuje podporu autorizace. Okamžitě po middlewaru Authentication.
Cookie Policy Sleduje souhlas uživatelů s ukládáním osobních údajů a vynucuje minimální standardy pro pole cookie, například secure a SameSite. Před middlewarem, který vydává soubory cookie. Příklady: Authentication, Session, MVC (TempData).
CORS Konfiguruje sdílení prostředků mezi zdroji (CORS). Před komponentami, které používají CORS. UseCors v současné době musí předcházet před UseResponseCaching, a to kvůli této chybě.
DeveloperExceptionPage Vygeneruje stránku s informacemi o chybách, které jsou určené jenom pro použití ve vývojovém prostředí. Před komponentami, které generují chyby. V případě vývojového prostředí šablony projektů automaticky zaregistrují tento middleware jako první middleware v kanálu.
Diagnostika Několik samostatných middlewarů, které poskytují stránku výjimek pro vývojáře, zpracování výjimek, stránky stavových kódů a výchozí webovou stránku pro nové aplikace. Před komponentami, které generují chyby. Terminál pro výjimky nebo obsluhu výchozí webové stránky pro nové aplikace.
Forwarded Headers Předá přeposílané hlavičky do aktuálního požadavku. Před komponentami, které využívají aktualizovaná pole. Příklady: Schéma, hostitel, IP adresa klienta, metoda.
Health Check Kontroluje stav aplikace ASP.NET Core a jejích závislostí, jako je kontrola dostupnosti databáze. Terminál, pokud požadavek odpovídá koncovému bodu kontroly stavu.
Header Propagation Šíří hlavičky HTTP z příchozího požadavku do odchozích požadavků klienta HTTP.
Protokolování HTTP Protokoluje požadavky HTTP a odpovědi. Na začátku kanálu middlewaru.
HTTP Method Override Umožňuje, aby příchozí požadavek POST přepsal metodu. Před komponentami, které využívají aktualizovanou metodu.
HTTPS Redirection Přesměruje všechny požadavky HTTP na HTTPS. Před komponentami, které využívají danou adresu URL.
HTTP Strict Transport Security (HSTS) Middleware pro vylepšení zabezpečení, který přidává speciální hlavičku odpovědi. Před odesláním odpovědí a po komponentách, které upravují požadavky. Příklady: Přeposílané hlavičky, přepsání adresy URL.
MVC Zpracovává požadavky s využitím MVC / Razor Pages. Terminál, pokud požadavek odpovídá trase.
OWIN Spolupracuje s aplikacemi, servery a middlewarem založenými na OWIN. Terminál, pokud middleware OWIN plně zpracuje daný požadavek.
Request Decompression Poskytuje podporu pro dekompresi požadavků. Před komponentami, které čtou text požadavku.
Ukládání odpovědí do mezipaměti Poskytuje podporu pro ukládání odpovědí do mezipaměti. Před komponentami, které vyžadují ukládání do mezipaměti. Je potřeba použít UseCORS před UseResponseCaching.
Response Compression Poskytuje podporu pro kompresi odpovědí. Před komponentami, které vyžadují kompresi.
Request Localization Poskytuje podporu lokalizace. Před komponentami citlivými na lokalizaci. Při použití RouteDataRequestCultureProvider musí následovat po middlewaru Routing.
Endpoint Routing Definuje a omezuje trasy požadavků. Terminál pro vyhovující trasy.
SPA Zpracovává všechny požadavky od tohoto bodu v řetězci middlewaru vrácením výchozí stránky pro jednostránkovou aplikaci (SPA). U konce řetězce, aby měl přednost ostatní middleware pro obsluhu statických souborů, akcí MVC atd.
Relace Poskytuje podporu pro správu uživatelských relací. Před komponentami, které vyžadují middleware Session.
Static Files Poskytuje podporu pro obsluhu statických souborů a procházení adresářů. Terminál, pokud požadavek odpovídá souboru.
Přepsání adresy URL Poskytuje podporu pro přepis adres URL a přesměrování požadavků. Před komponentami, které využívají danou adresu URL.
W3CLogging Generuje protokoly přístupu k serveru v rozšířeném formátu souborů protokolu W3C. Na začátku kanálu middlewaru.
Webové sokety Povoluje protokol WebSocket. Před komponentami, které jsou potřeba k přijetí požadavků WebSocket.

Další materiály

Autor: Rick Anderson a Steve Smith

Middleware je software sestavený do kanálu aplikace pro zpracování požadavků a odpovědí. Každá komponenta:

  • Určuje, jestli se má požadavek předat další komponentě v kanálu.
  • Může provádět práci před a za další komponentou v kanálu.

K sestavení kanálu požadavků se používají delegáti požadavků. Delegáti požadavků zpracovávají každý požadavek HTTP.

Delegáti požadavků se konfigurují pomocí rozšiřujících metod Run, Map a Use. Jednotlivý delegát požadavku může být zadán na řádku jako anonymní metoda (tzv. in-line middleware) nebo může být definován v opakovaně použitelné třídě. Tyto opakovaně použitelné třídy a vložené anonymní metody jsou middleware, označované také jako komponenty middlewaru. Každá komponenta middlewaru v kanálu požadavků zodpovídá za vyvolání další komponenty v kanálu nebo zkratování kanálu. Když middleware zkratuje, označuje se jako terminální middleware, protože brání dalšímu middlewaru ve zpracování požadavku.

Téma věnované migraci obslužných rutin HTTP a modulů do middlewaru ASP.NET Core vysvětluje rozdíl mezi kanály požadavků v prostředích ASP.NET Core a ASP.NET 4.x a poskytuje další ukázky middlewaru.

Vytvoření kanálu middlewaru s využitím IApplicationBuilderu

Kanál požadavků ASP.NET Core se skládá z posloupnosti delegátů požadavků, kteří se volají jeden po druhém. Tento koncept demonstruje následující schéma. Vlákno spouštění postupuje podle černých šipek.

Vzor zpracování požadavku zobrazující příchozí požadavek, jeho zpracování třemi middlewary a odpověď opouštějící aplikaci. Každý middleware spustí svou logiku a předá požadavek dalšímu middlewaru v příkazu next(). Poté, co třetí middleware zpracuje požadavek, projde požadavek zpět přes předchozí dva middlewary v opačném pořadí k dalšímu zpracování po příkazech next() a poté opustí aplikaci jako odpověď klientovi.

Každý delegát může provádět operace před a za dalším delegátem. Delegáti zpracování výjimek by se měli volat v rané fázi zpracování kanálu, aby mohli zachytit výjimky, ke kterým dochází v pozdějších fázích zpracování kanálu.

Nejjednodušší možná aplikace ASP.NET Core nastavuje jednoho delegáta požadavků, který zpracovává všechny požadavky. Tento případ nezahrnuje vlastní kanál požadavků. Místo toho se jako reakce na každý požadavek HTTP volá jedna anonymní funkce.

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

Zřetězte několik delegátů požadavků společně s Use. Parametr next představuje dalšího delegáta v kanálu. Kanál můžete zkratovat tak, že nevoláte parametr next. Obvykle můžete provádět akce před dalším delegátem i po něm, jak ukazuje následující příklad:

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

Pokud delegát nepředá požadavek dalšímu delegátovi, označuje se to jako zkratování kanálu požadavků. Zkratování je často žádoucí, protože zabraňuje zbytečné práci. Middleware se statickými soubory může například fungovat jako terminální middleware tím, že zpracuje požadavek pro statický soubor a zbytek kanálu zkratuje. Middleware přidaný do kanálu před middleware, který ukončí další zpracování, stále zpracovává kód po příslušných příkazech next.Invoke. Ohledně pokusu o zápis do již odeslané odpovědi si však projděte následující upozornění.

Upozorňující

Po odeslání odpovědi klientovi nevolejte next.Invoke. Změny v HttpResponse po spuštění odpovědi vyvolají výjimku. Například nastavení hlaviček a stavového kódu vyvolá výjimku. Zápis do textu odpovědi po volání next:

  • Může způsobit porušení protokolu. Příklad: Delší zápis, než je stanovená délka Content-Length.
  • Může poškodit formát textu. Příklad: Zápis zápatí HTML do souboru CSS.

HasStarted je užitečná nápověda indikující, jestli byly odeslány hlavičky nebo zapisováno do textu.

Delegáti Run neobdrží parametr next. První delegát Run je vždy terminální a ukončí kanál. Použití Run je obvyklá konvence. Některé komponenty middlewaru můžou zpřístupnit metody Run[Middleware], které běží na konci kanálu:

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

Pokud chcete zobrazit komentáře ke kódu přeložené do jiných jazyků, než je angličtina, dejte nám vědět v této diskuzi na GitHubu.

V předchozím příkladu delegát Run zapíše "Hello from 2nd delegate." do odpovědi a pak ukončí kanál. Pokud je za delegáta Run přidán jiný delegát Use nebo Run, nevolá se.

Pořadí middlewaru

Následující diagram znázorňuje kompletní kanál zpracování požadavků pro aplikace ASP.NET Core MVC a Razor Pages. Můžete si prohlédnout, jak je v typické aplikaci uspořádané stávající middlewary seřazené a kde se přidávají vlastní middlewary. Máte plnou kontrolu nad tím, jak změnit pořadí stávajících middlewarů nebo vložit nové vlastní middlewary podle potřeby vašich scénářů.

Kanál middlewaru ASP.NET Core

Middleware Endpoint v předchozím diagramu spustí kanál filtru pro odpovídající typ aplikace – MVC nebo Razor Pages.

Kanál filtru ASP.NET Core

Pořadí, ve kterém jsou komponenty middlewaru přidány do metody Startup.Configure, definuje pořadí, ve kterém se komponenty middlewaru vyvolávají pro požadavky, a obrácené pořadí pro odpovědi. Toto pořadí má zásadní důležitost pro zabezpečení, výkon a funkce.

Následující metoda Startup.Configure přidává komponenty middlewaru související se zabezpečením v typickém doporučeném pořadí:

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

V předchozím kódu:

  • Middleware, který se nepřidá při vytváření nové webové aplikace s účty jednotlivých uživatelů, je zakomentovaný.
  • Ne každý middleware se objevuje přesně v tomto pořadí, ale mnoho z nich ano. Příklad:
    • UseCors, UseAuthentication a UseAuthorization musí být v uvedeném pořadí.
    • UseCors v současné době musí předcházet před UseResponseCaching, a to kvůli této chybě.
    • UseRequestLocalization musí být uvedený před jakýmkoli jiným middlewarem, který kontroluje kulturu požadavků (například app.UseMvcWithDefaultRoute()).

V některých scénářích má middleware jiné řazení. Například pořadí ukládání do mezipaměti a komprese je specifické pro daný scénář a existuje několik platných pořadí. Příklad:

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

V předchozím kódu by bylo možné snížit využití procesoru uložením komprimované odpovědi do mezipaměti, ale mohlo by se stát, že budete ukládat do mezipaměti více reprezentací prostředku pomocí různých kompresních algoritmů, jako jsou například Gzip nebo Brotli.

Následující pořadí kombinuje statické soubory, aby se umožnilo ukládání komprimovaných statických souborů do mezipaměti:

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

Následující metoda Startup.Configure přidá komponenty middlewaru pro běžné scénáře aplikací:

  1. Zpracování výjimek nebo chyb
    • Když se aplikace spustí ve vývojovém prostředí:
      • Middleware Developer Exception Page (UseDeveloperExceptionPage) hlásí běhové chyby aplikací.
      • Middleware Database Error Page hlásí běhové chyby databází.
    • Když se aplikace spustí v produkčním prostředí:
      • Middleware Exception Handler (UseExceptionHandler) zachycuje výjimky vyvolané v následujících middlewarech.
      • Middleware HSTS (HTTP Strict Transport Security Protocol) (UseHsts) přidává hlavičku Strict-Transport-Security.
  2. Middleware HTTPS Redirection (UseHttpsRedirection) přesměruje požadavky HTTP na HTTPS.
  3. Middleware Static File (UseStaticFiles) vrací statické soubory a zkratuje další zpracování požadavků.
  4. Middleware Cookie Policy (UseCookiePolicy) zajišťuje soulad aplikace s Obecným nařízením EU o ochraně osobních údajů (GDPR).
  5. Middleware Routing (UseRouting) slouží ke směrování požadavků.
  6. Middleware Authentication (UseAuthentication) se pokusí ověřit uživatele předtím, než se mu povolí přístup k zabezpečeným prostředkům.
  7. Middleware Authorization (UseAuthorization) autorizuje uživatele pro přístup k zabezpečeným prostředkům.
  8. Middleware Session (UseSession) nastavuje a udržuje stav relace. Pokud aplikace používá stav relace, volejte middleware Session za middlewarem Cookie Policy a před middlewarem MVC.
  9. Middleware Endpoint Routing (UseEndpoints s MapRazorPages) slouží pro přidání koncových bodů Razor Pages do kanálu požadavků.
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();
    });
}

V předchozím ukázkovém kódu je každá rozšiřující metoda middlewaru zpřístupněná v IApplicationBuilder prostřednictvím oboru názvů Microsoft.AspNetCore.Builder.

UseExceptionHandler je první komponenta middlewaru přidaná do kanálu. Proto middleware Exception Handler zachycuje všechny výjimky, ke kterým dochází v pozdějších voláních.

Middleware Static File se volá v rané fázi zpracování kanálu, aby mohl zpracovávat požadavky a provést zkratování, aniž by se musely projít zbývající komponenty. Middleware Static File neposkytuje žádné kontroly autorizace. Všechny soubory obsluhované middlewarem Static File, včetně souborů pod wwwroot, jsou veřejně dostupné. Informace o přístupu k zabezpečení statických souborů najdete v tématu Statické soubory v ASP.NET Core.

Pokud požadavek nezpracuje middleware Static File, předá se do middlewaru Authentication (UseAuthentication), který provádí ověřování. Ověřování nezpůsobí zkratování neověřených požadavků. I když middleware Authentication ověřuje požadavky, autorizace (a zamítnutí) nastane až poté, co MVC vybere konkrétní kontroler MVC nebo Razor Pages a akci.

Následující příklad ukazuje pořadí middlewaru, ve kterém požadavky pro statické soubory zpracovává middleware Static File před middlewarem Response Compression. Statické soubory nejsou při tomto uspořádání middlewaru komprimovány. Odpovědi Razor Pages je možné komprimovat.

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();
    });
}

Pro jednostránkové aplikace (SPA) je middleware SPA UseSpaStaticFiles obvykle v kanálu middlewaru poslední. Middleware SPA je poslední:

  • Aby se všem ostatním middlewarům umožnilo reagovat na odpovídající požadavky jako první.
  • Aby se jednostránkovým aplikacím se směrováním na straně klienta umožnilo spouštění pro všechny trasy, které aplikace serveru nerozpozná.

Další podrobnosti o jednostránkových aplikacích najdete v průvodcích pro šablony projektů React a Angular.

Pořadí middlewaru Forwarded Headers

Middleware Forwarded Headers by měl běžet před ostatním middlewarem. Toto řazení zajišťuje, že middleware, který spoléhá na informace předávaných hlaviček, může využívat hodnoty hlaviček ke zpracování. Pokud chcete middleware Forwarded Headers spustit po diagnostice a middlewaru pro zpracování chyb, přečtěte si téma Middleware Ordered Headers.

Větvení kanálu middlewaru

Rozšíření Map se používají jako konvence pro větvení kanálu. Map rozvětví kanál požadavku na základě shod dané cesty k požadavku. Pokud cesta k požadavku začíná danou cestou, větev se spustí.

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

Následující tabulka ukazuje požadavky a odpovědi z http://localhost:1234 s využitím předchozího kódu.

Požádat Response
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.

Při použití Map se odpovídající segmenty cest odeberou z HttpRequest.Path a připojí ke HttpRequest.PathBase pro každý požadavek.

Map podporuje vnoření, například:

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map také může odpovídat více segmentům najednou:

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 rozvětví kanál požadavku na základě výsledku daného predikátu. K mapování požadavků na novou větev kanálu je možné použít jakýkoli predikát typu Func<HttpContext, bool>. V následujícím příkladu se predikát používá ke zjištění přítomnosti proměnné řetězce dotazu 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.");
        });
    }
}

Následující tabulka ukazuje požadavky a odpovědi z http://localhost:1234 s využitím předchozího kódu:

Požádat Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

UseWhen také rozvětví kanál požadavku na základě výsledku daného predikátu. Na rozdíl od MapWhen se tato větev znovu připojí k hlavnímu kanálu, pokud nezpůsobí zkratování nebo neobsahuje terminální middleware:

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

V předchozím příkladu se pro všechny požadavky zapíše odpověď Hello from main pipeline. Pokud požadavek obsahuje proměnnou řetězce dotazu branch, před opětovným připojením k hlavnímu kanálu se zaprotokoluje její hodnota.

Integrovaný middleware

ASP.NET Core se dodává s následujícími komponentami middlewaru. Sloupec Pořadí obsahuje poznámky k umístění middlewaru v kanálu zpracování požadavků a to, za jakých podmínek může middleware ukončit zpracování požadavků. Když middleware zkratuje kanál zpracování požadavků a zabrání dalšímu navazujícímu middlewaru zpracovat požadavek, označuje se jako terminální middleware. Další informace o zkratování najdete v části Vytvoření kanálu middlewaru s využitím IApplicationBuilderu.

Middleware Popis Objednávka
Authentication Poskytuje podporu ověřování. Než je potřeba HttpContext.User. Terminál pro zpětné volání OAuth.
Autorizace Poskytuje podporu autorizace. Okamžitě po middlewaru Authentication.
Cookie Policy Sleduje souhlas uživatelů s ukládáním osobních údajů a vynucuje minimální standardy pro pole cookie, například secure a SameSite. Před middlewarem, který vydává soubory cookie. Příklady: Authentication, Session, MVC (TempData).
CORS Konfiguruje sdílení prostředků mezi zdroji (CORS). Před komponentami, které používají CORS. UseCors v současné době musí předcházet před UseResponseCaching, a to kvůli této chybě.
Diagnostika Několik samostatných middlewarů, které poskytují stránku výjimek pro vývojáře, zpracování výjimek, stránky stavových kódů a výchozí webovou stránku pro nové aplikace. Před komponentami, které generují chyby. Terminál pro výjimky nebo obsluhu výchozí webové stránky pro nové aplikace.
Forwarded Headers Předá přeposílané hlavičky do aktuálního požadavku. Před komponentami, které využívají aktualizovaná pole. Příklady: Schéma, hostitel, IP adresa klienta, metoda.
Health Check Kontroluje stav aplikace ASP.NET Core a jejích závislostí, jako je kontrola dostupnosti databáze. Terminál, pokud požadavek odpovídá koncovému bodu kontroly stavu.
Header Propagation Šíří hlavičky HTTP z příchozího požadavku do odchozích požadavků klienta HTTP.
HTTP Method Override Umožňuje, aby příchozí požadavek POST přepsal metodu. Před komponentami, které využívají aktualizovanou metodu.
HTTPS Redirection Přesměruje všechny požadavky HTTP na HTTPS. Před komponentami, které využívají danou adresu URL.
HTTP Strict Transport Security (HSTS) Middleware pro vylepšení zabezpečení, který přidává speciální hlavičku odpovědi. Před odesláním odpovědí a po komponentách, které upravují požadavky. Příklady: Přeposílané hlavičky, přepsání adresy URL.
MVC Zpracovává požadavky s využitím MVC / Razor Pages. Terminál, pokud požadavek odpovídá trase.
OWIN Spolupracuje s aplikacemi, servery a middlewarem založenými na OWIN. Terminál, pokud middleware OWIN plně zpracuje daný požadavek.
Ukládání odpovědí do mezipaměti Poskytuje podporu pro ukládání odpovědí do mezipaměti. Před komponentami, které vyžadují ukládání do mezipaměti. Je potřeba použít UseCORS před UseResponseCaching.
Response Compression Poskytuje podporu pro kompresi odpovědí. Před komponentami, které vyžadují kompresi.
Request Localization Poskytuje podporu lokalizace. Před komponentami citlivými na lokalizaci. Při použití RouteDataRequestCultureProvider musí následovat po middlewaru Routing.
Endpoint Routing Definuje a omezuje trasy požadavků. Terminál pro vyhovující trasy.
SPA Zpracovává všechny požadavky od tohoto bodu v řetězci middlewaru vrácením výchozí stránky pro jednostránkovou aplikaci (SPA). U konce řetězce, aby měl přednost ostatní middleware pro obsluhu statických souborů, akcí MVC atd.
Relace Poskytuje podporu pro správu uživatelských relací. Před komponentami, které vyžadují middleware Session.
Static Files Poskytuje podporu pro obsluhu statických souborů a procházení adresářů. Terminál, pokud požadavek odpovídá souboru.
Přepsání adresy URL Poskytuje podporu pro přepis adres URL a přesměrování požadavků. Před komponentami, které využívají danou adresu URL.
Webové sokety Povoluje protokol WebSocket. Před komponentami, které jsou potřeba k přijetí požadavků WebSocket.

Další materiály