Sdílet prostřednictvím


ASP.NET Core Middleware

Poznámka:

Toto není nejnovější verze tohoto článku. Pro aktuální verzi tohoto článku se podívejte na verzi .NET 9.

Varování

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Pro aktuální verzi tohoto článku se podívejte na verzi .NET 9.

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í.

Pro aktuální verzi tohoto článku se podívejte na verzi .NET 9.

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ší součásti v potrubí.
  • Může provádět práci před a po další komponentě v řetězci.

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ů jsou konfigurováni 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 způsobí zkrat, označuje se jako terminální middleware, protože brání další middleware ve zpracování požadavku.

Migrace modulů HTTP do middlewaru ASP.NET Core vysvětluje rozdíl mezi kanály požadavků v ASP.NET Core a ASP.NET 4.x a poskytuje další ukázky middlewaru.

Role middlewaru podle typu aplikace

Blazor Web Apps, Razor Pages a MVC zpracovávají požadavky prohlížeče na serveru pomocí middlewaru. Pokyny v tomto článku platí pro tyto typy aplikací.

Samostatné Blazor WebAssembly aplikace běží zcela na klientovi a nezpracují požadavky pomocí kanálu middlewaru. Pokyny v tomto článku se nevztahují na samostatné Blazor WebAssembly aplikace.

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ů v ASP.NET Core se skládá z posloupnosti delegátů požadavků, kteří se volají postupně. 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 počátečních fázích řetězce, aby mohli zachytit výjimky, ke kterým dochází v pozdějších fázích řetězce.

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

Propojte více delegátů požadavků pomocí Use. Parametr next představuje dalšího delegáta v potrubí. Kanál můžete zkratovat tím, že nezavoláte parametr next. 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í zpracovatelského kanálu požadavků

Pokud delegát nepředá požadavek dalšímu delegátovi, označuje se to jako přerušení kanálu požadavků. Zkratování je často žádoucí, protože zabraňuje zbytečné práci. Například middleware statického souboru může fungovat jako terminálový middleware zpracováním požadavku na statický soubor a přeskočením zbytku zpracování. 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í.

Varování

Nevolejte next.Invoke během odesílání odpovědi nebo po jejím odeslání klientovi. Po spuštění HttpResponse změny vyústí ve 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 zapsání vyšší hodnoty, 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 předčasným přerušením po směrování.

Run delegáti

Delegáti Run neobdrží parametr next. První delegát Run je vždy terminální a ukončí zpracovatelský řetězec. 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 Use přidán jiný delegát Run nebo Run, nevolá se.

Preferujte přetížení metody app.Use, které vyžaduje předání kontextu dalšímu kroku

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

  • Vyžaduje předání kontextu do next.
  • Šetří dvě alokace paměti na jednotlivé požadavky, které jsou vyžadovány při použití jiné přetížené funkce.

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ářů.

Potrubí 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 statickými soubory. 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 objevit před jakýmkoli middlewarem, který může zkontrolovat jazykovou verzi požadavku, například app.UseStaticFiles().
    • UseRateLimiter musí být volána po UseRouting, když se používají rozhraní API pro omezení rychlosti pro konkrétní koncové body. Pokud je například atribut [EnableRateLimiting] použit, UseRateLimiter musí být zavolána po UseRouting. Při volání pouze globálních limiterů UseRateLimiter může být volán 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 pro statické soubory (UseStaticFiles) vrací statické soubory a přeruší 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. Routing Middleware (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. Autorizační Middleware (UseAuthorization) opravňuje uživatele ke vstupu do zabezpečených prostředků.
  8. Session Middleware (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í middleware komponenta přidaná do potrubí. 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í přeskočení 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í Razor stránku nebo kontroler MVC 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 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 z dopředných hlaviček, může využívat jejich hodnoty pro 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.

Rozvětvení middleware kanálu

Rozšíření Map se používají jako konvence pro větvení kanálu. Map rozvětví požadavkový kanál na základě shody dané cesty požadavku. Pokud cesta žádosti začíná zadanou 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žadavek Reakce
localhost:1234 Zdravím vás od delegáta mimo Mapu.
localhost:1234/map1 Test mapy 1
localhost:1234/map2 Test mapy 2
localhost:1234/map3 Zdravím vás od delegáta mimo Mapu.

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žadavek Reakce
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 MapWhense tato větev znovu připojí k hlavnímu potrubí, pokud 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
Ochrana proti padělání Poskytuje podporu pro ochranu proti falšování požadavků. Po ověření a autorizaci, předtím než se přejde ke koncovým bodům.
Autentizace Poskytuje podporu ověřování. Než bude potřeba HttpContext.User. Terminál pro zpětné volání OAuth.
Autorizace Poskytuje podporu při autorizaci. Okamžitě po middlewaru Authentication.
Cookie Politika 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. Než middleware začne vydávat soubory cookie. Příklady: Authentication, Session, MVC (TempData).
CORS Provádí konfiguraci 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. Pokud je prostředí ve vývoji, šablony projektů automaticky zaregistrují tento middleware jako první middleware v pipeline.
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.
Přesměrované hlavičky Přesměruje přeposílané hlavičky na aktuální požadavek. Před komponentami, které využívají aktualizovaná pole. Příklady: Schéma, hostitel, IP adresa klienta, metoda.
Kontrola stavu 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.
Šíření protokolových hlaviček Šíří 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 potrubí middlewaru.
Přepsání metody HTTP Umožňuje, aby příchozí požadavek POST změnil metodu. Před komponentami, které využívají aktualizovanou metodu.
Přesměrování HTTPS Přesměruje všechny požadavky HTTP na HTTPS. Před komponentami, které využívají danou adresu URL.
Zabezpečení striktního přenosu HTTP (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 Interoperabilita s aplikacemi, servery a middlewarem založenými na OWIN. Terminál, pokud middleware OWIN plně zpracuje daný požadavek.
Ukládání výstupu do mezipaměti Poskytuje podporu pro ukládání odpovědí do mezipaměti na základě konfigurace. Před komponentami, které vyžadují cacheování. 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í cacheování. Je potřeba použít UseCORS před UseResponseCaching. Aplikace uživatelského rozhraní, jako jsou například Razor Pages, obvykle nebývají 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í.
Vyžádat dekompresi Poskytuje podporu pro dekompresi požadavků. Před komponentami, které čtou text požadavku.
Komprese odpovědí Poskytuje podporu pro kompresi odpovědí. Před komponentami, které vyžadují kompresi.
Žádost o lokalizaci 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.
Směrování koncového bodu Definuje a omezuje trasy požadavků. Terminál pro vyhovující trasy.
LÁZNĚ 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). Na konci řetězce, aby mělo přednost ostatní middleware pro obsluhu statických souborů, akcí MVC atd.
Sezení Poskytuje podporu pro správu uživatelských relací. Před komponentami, které vyžadují Session.
Statické soubory 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 potrubí middlewaru.
Webové sokety Povoluje protokol WebSocket. Před komponentami, které jsou nezbytné pro 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ší součásti v potrubí.
  • Může provádět práci před a po další komponentě v řetězci.

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ů jsou konfigurováni 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 způsobí zkrat, označuje se jako terminální middleware, protože brání další middleware ve zpracování požadavku.

Migrace modulů HTTP do middlewaru ASP.NET Core vysvětluje rozdíl mezi kanály požadavků v ASP.NET Core a ASP.NET 4.x a poskytuje další ukázky middlewaru.

Role middlewaru podle typu aplikace

Razor Pages, MVC, Blazor Servera hostovaný projekt na straně serveru řešení Blazor WebAssembly, které zpracovává požadavky prohlížeče na serveru s middlewarem. Pokyny v tomto článku platí pro tyto typy aplikací.

Samostatné Blazor WebAssembly aplikace běží zcela na klientovi a nezpracují požadavky pomocí kanálu middlewaru. Pokyny v tomto článku se nevztahují na samostatné Blazor WebAssembly aplikace.

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ů v ASP.NET Core se skládá z posloupnosti delegátů požadavků, kteří se volají postupně. 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 počátečních fázích řetězce, aby mohli zachytit výjimky, ke kterým dochází v pozdějších fázích řetězce.

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

Propojte více delegátů požadavků pomocí Use. Parametr next představuje dalšího delegáta v potrubí. Kanál můžete zkratovat tím, že nezavoláte parametr next. 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 přerušení kanálu požadavků. Zkratování je často žádoucí, protože zabraňuje zbytečné práci. Například middleware statického souboru může fungovat jako terminálový middleware zpracováním požadavku na statický soubor a přeskočením zbytku zpracování. 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í.

Varování

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čí zpracovatelský řetězec. 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 Use přidán jiný delegát Run nebo Run, nevolá se.

Preferujte přetížení metody app.Use, které vyžaduje předání kontextu dalšímu kroku

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

  • Vyžaduje předání kontextu do next.
  • Šetří dvě alokace paměti na jednotlivé požadavky, které jsou vyžadovány při použití jiné přetížené funkce.

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ářů.

Potrubí 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 statickými soubory. 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 objevit před jakýmkoli middlewarem, který může zkontrolovat jazykovou verzi požadavku, například app.UseStaticFiles().
    • UseRateLimiter musí být volána po UseRouting, když se používají rozhraní API pro omezení rychlosti pro konkrétní koncové body. Pokud je například atribut [EnableRateLimiting] použit, UseRateLimiter musí být zavolána po UseRouting. Při volání pouze globálních limiterů UseRateLimiter může být volán 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 pro statické soubory (UseStaticFiles) vrací statické soubory a přeruší 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. Routing Middleware (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. Autorizační Middleware (UseAuthorization) opravňuje uživatele ke vstupu do zabezpečených prostředků.
  8. Session Middleware (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í middleware komponenta přidaná do potrubí. 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í přeskočení 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í Razor stránku nebo kontroler MVC 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 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 z dopředných hlaviček, může využívat jejich hodnoty pro 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.

Rozvětvení middleware kanálu

Rozšíření Map se používají jako konvence pro větvení kanálu. Map rozvětví požadavkový kanál na základě shody dané cesty požadavku. Pokud cesta žádosti začíná zadanou 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žadavek Reakce
localhost:1234 Zdravím vás od delegáta mimo Mapu.
localhost:1234/map1 Test mapy 1
localhost:1234/map2 Test mapy 2
localhost:1234/map3 Zdravím vás od delegáta mimo Mapu.

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žadavek Reakce
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
Autentizace Poskytuje podporu ověřování. Než bude potřeba HttpContext.User. Terminál pro zpětné volání OAuth.
Autorizace Poskytuje podporu při autorizaci. Okamžitě po middlewaru Authentication.
Cookie Politika 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. Než middleware začne vydávat soubory cookie. Příklady: Authentication, Session, MVC (TempData).
CORS Provádí konfiguraci 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. Pokud je prostředí ve vývoji, šablony projektů automaticky zaregistrují tento middleware jako první middleware v pipeline.
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.
Přesměrované hlavičky Přesměruje přeposílané hlavičky na aktuální požadavek. Před komponentami, které využívají aktualizovaná pole. Příklady: Schéma, hostitel, IP adresa klienta, metoda.
Kontrola stavu 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.
Šíření protokolových hlaviček Šíří 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 potrubí middlewaru.
Přepsání metody HTTP Umožňuje, aby příchozí požadavek POST změnil metodu. Před komponentami, které využívají aktualizovanou metodu.
Přesměrování HTTPS Přesměruje všechny požadavky HTTP na HTTPS. Před komponentami, které využívají danou adresu URL.
Zabezpečení striktního přenosu HTTP (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 Interoperabilita s aplikacemi, servery a middlewarem založenými na OWIN. Terminál, pokud middleware OWIN plně zpracuje daný požadavek.
Ukládání výstupu do mezipaměti Poskytuje podporu pro ukládání odpovědí do mezipaměti na základě konfigurace. Před komponentami, které vyžadují cacheování. 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í cacheování. Je potřeba použít UseCORS před UseResponseCaching. Aplikace uživatelského rozhraní, jako jsou například Razor Pages, obvykle nebývají 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í.
Vyžádat dekompresi Poskytuje podporu pro dekompresi požadavků. Před komponentami, které čtou text požadavku.
Komprese odpovědí Poskytuje podporu pro kompresi odpovědí. Před komponentami, které vyžadují kompresi.
Žádost o lokalizaci Poskytuje podporu lokalizace. Před komponentami citlivými na lokalizaci. Při použití RouteDataRequestCultureProvider musí následovat po middlewaru Routing.
Směrování koncového bodu Definuje a omezuje trasy požadavků. Terminál pro vyhovující trasy.
LÁZNĚ 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). Na konci řetězce, aby mělo přednost ostatní middleware pro obsluhu statických souborů, akcí MVC atd.
Sezení Poskytuje podporu pro správu uživatelských relací. Před komponentami, které vyžadují Session.
Statické soubory 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 potrubí middlewaru.
Webové sokety Povoluje protokol WebSocket. Před komponentami, které jsou nezbytné pro 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ší součásti v potrubí.
  • Může provádět práci před a po další komponentě v řetězci.

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ů jsou konfigurováni 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 způsobí zkrat, označuje se jako terminální middleware, protože brání další middleware ve zpracování požadavku.

Migrace modulů HTTP do middlewaru ASP.NET Core vysvětluje rozdíl mezi kanály požadavků v 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ů v ASP.NET Core se skládá z posloupnosti delegátů požadavků, kteří se volají postupně. 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 počátečních fázích řetězce, aby mohli zachytit výjimky, ke kterým dochází v pozdějších fázích řetězce.

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

Propojte více delegátů požadavků pomocí Use. Parametr next představuje dalšího delegáta v potrubí. Kanál můžete zkratovat tím, že nezavoláte parametr next. 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 přerušení kanálu požadavků. Zkratování je často žádoucí, protože zabraňuje zbytečné práci. Například middleware statického souboru může fungovat jako terminálový middleware zpracováním požadavku na statický soubor a přeskočením zbytku zpracování. 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í.

Varování

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čí zpracovatelský řetězec. 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 Use přidán jiný delegát Run nebo Run, nevolá se.

Preferujte přetížení metody app.Use, které vyžaduje předání kontextu dalšímu kroku

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

  • Vyžaduje předání kontextu do next.
  • Šetří dvě alokace paměti na jednotlivé požadavky, které jsou vyžadovány při použití jiné přetížené funkce.

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ářů.

Potrubí 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 statickými soubory. 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 pro statické soubory (UseStaticFiles) vrací statické soubory a přeruší 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. Routing Middleware (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. Autorizační Middleware (UseAuthorization) opravňuje uživatele ke vstupu do zabezpečených prostředků.
  8. Session Middleware (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í middleware komponenta přidaná do potrubí. 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í přeskočení 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í Razor stránku nebo kontroler MVC 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 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 z dopředných hlaviček, může využívat jejich hodnoty pro 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.

Rozvětvení middleware kanálu

Rozšíření Map se používají jako konvence pro větvení kanálu. Map rozvětví požadavkový kanál na základě shody dané cesty požadavku. Pokud cesta žádosti začíná zadanou 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žadavek Reakce
localhost:1234 Zdravím vás od delegáta mimo Mapu.
localhost:1234/map1 Test mapy 1
localhost:1234/map2 Test mapy 2
localhost:1234/map3 Zdravím vás od delegáta mimo Mapu.

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žadavek Reakce
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
Autentizace Poskytuje podporu ověřování. Než bude potřeba HttpContext.User. Terminál pro zpětné volání OAuth.
Autorizace Poskytuje podporu při autorizaci. Okamžitě po middlewaru Authentication.
Cookie Politika 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. Než middleware začne vydávat soubory cookie. Příklady: Authentication, Session, MVC (TempData).
CORS Provádí konfiguraci 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. Pokud je prostředí ve vývoji, šablony projektů automaticky zaregistrují tento middleware jako první middleware v pipeline.
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.
Přesměrované hlavičky Přesměruje přeposílané hlavičky na aktuální požadavek. Před komponentami, které využívají aktualizovaná pole. Příklady: Schéma, hostitel, IP adresa klienta, metoda.
Kontrola stavu 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.
Šíření protokolových hlaviček Šíří 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 potrubí middlewaru.
Přepsání metody HTTP Umožňuje, aby příchozí požadavek POST změnil metodu. Před komponentami, které využívají aktualizovanou metodu.
Přesměrování HTTPS Přesměruje všechny požadavky HTTP na HTTPS. Před komponentami, které využívají danou adresu URL.
Zabezpečení striktního přenosu HTTP (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 Interoperabilita s aplikacemi, servery a middlewarem založenými na OWIN. Terminál, pokud middleware OWIN plně zpracuje daný požadavek.
Vyžádat dekompresi 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í cacheování. Je potřeba použít UseCORS před UseResponseCaching.
Komprese odpovědí Poskytuje podporu pro kompresi odpovědí. Před komponentami, které vyžadují kompresi.
Žádost o lokalizaci Poskytuje podporu lokalizace. Před komponentami citlivými na lokalizaci. Při použití RouteDataRequestCultureProvider musí následovat po middlewaru Routing.
Směrování koncového bodu Definuje a omezuje trasy požadavků. Terminál pro vyhovující trasy.
LÁZNĚ 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). Na konci řetězce, aby mělo přednost ostatní middleware pro obsluhu statických souborů, akcí MVC atd.
Sezení Poskytuje podporu pro správu uživatelských relací. Před komponentami, které vyžadují Session.
Statické soubory 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 potrubí middlewaru.
Webové sokety Povoluje protokol WebSocket. Před komponentami, které jsou nezbytné pro 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ší součásti v potrubí.
  • Může provádět práci před a po další komponentě v řetězci.

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ů jsou konfigurováni 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 způsobí zkrat, označuje se jako terminální middleware, protože brání další middleware ve zpracování požadavku.

Migrace modulů HTTP do middlewaru ASP.NET Core vysvětluje rozdíl mezi kanály požadavků v 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ů v ASP.NET Core se skládá z posloupnosti delegátů požadavků, kteří se volají postupně. 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 počátečních fázích řetězce, aby mohli zachytit výjimky, ke kterým dochází v pozdějších fázích řetězce.

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

Propojte více delegátů požadavků pomocí Use. Parametr next představuje dalšího delegáta v potrubí. Potrubí můžete zkratovat tím, že nezavolá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 přerušení kanálu požadavků. Zkratování je často žádoucí, protože zabraňuje zbytečné práci. Například middleware statického souboru může fungovat jako terminálový middleware zpracováním požadavku na statický soubor a přeskočením zbytku zpracování. 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í.

Varování

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čí zpracovatelský řetězec. 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 Use přidán jiný delegát Run 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ářů.

Potrubí 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 pro statické soubory (UseStaticFiles) vrací statické soubory a přeruší 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. Routing Middleware (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. Autorizační Middleware (UseAuthorization) opravňuje uživatele ke vstupu do zabezpečených prostředků.
  8. Session Middleware (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í middleware komponenta přidaná do potrubí. 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í přeskočení 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í Razor stránku nebo kontroler MVC 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 na konci middlewarového kanálu. Middleware SPA je poslední:

  • Aby se všem ostatním middlewarům umožnilo reagovat na odpovídající požadavky jako první.
  • Aby jednostránkovým aplikacím se směrováním na straně klienta bylo umožněno spouštět se na všech trasách, které nejsou rozpoznány serverovou aplikací.

Další podrobnosti o SPA 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 z dopředných hlaviček, může využívat jejich hodnoty pro 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.

Rozvětvení middleware kanálu

Rozšíření Map se používají jako konvence pro větvení kanálu. Map rozvětví požadavkový kanál na základě shody dané cesty požadavku. Pokud cesta žádosti začíná zadanou 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žadavek Reakce
localhost:1234 Zdravím vás od delegáta mimo Mapu.
localhost:1234/map1 Test mapy 1
localhost:1234/map2 Test mapy 2
localhost:1234/map3 Zdravím vás od delegáta mimo Mapu.

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žadavek Reakce
localhost:1234 Zdravím vás od delegáta mimo Mapu.
localhost:1234/?branch=main Použitá větev = 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
Ochrana proti padělání Poskytuje podporu pro ochranu proti falšování požadavků. Po ověření a autorizaci, předtím než se přejde ke koncovým bodům.
Autentizace Poskytuje podporu ověřování. Než bude potřeba HttpContext.User. Terminál pro zpětné volání OAuth.
Autorizace Poskytuje podporu při autorizaci. Okamžitě po middlewaru Authentication.
Cookie Politika 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. Než middleware začne vydávat soubory cookie. Příklady: Authentication, Session, MVC (TempData).
CORS Provádí konfiguraci 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.
Přesměrované hlavičky Přesměruje přeposílané hlavičky na aktuální požadavek. Před komponentami, které využívají aktualizovaná pole. Příklady: Schéma, hostitel, IP adresa klienta, metoda.
Kontrola stavu 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.
Šíření protokolových hlaviček Šíří hlavičky HTTP z příchozího požadavku do odchozích požadavků klienta HTTP.
Přepsání metody HTTP Umožňuje, aby příchozí požadavek POST změnil metodu. Před komponentami, které využívají aktualizovanou metodu.
Přesměrování HTTPS Přesměruje všechny požadavky HTTP na HTTPS. Před komponentami, které využívají danou adresu URL.
Zabezpečení striktního přenosu HTTP (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 Interoperabilita 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í cacheování. Je potřeba použít UseCORS před UseResponseCaching.
Komprese odpovědí Poskytuje podporu pro kompresi odpovědí. Před komponentami, které vyžadují kompresi.
Žádost o lokalizaci Poskytuje podporu lokalizace. Před komponentami citlivými na lokalizaci. Při použití RouteDataRequestCultureProvider musí následovat po middlewaru Routing.
Směrování koncového bodu Definuje a omezuje trasy požadavků. Terminál pro vyhovující trasy.
LÁZNĚ 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). Na konci řetězce, aby mělo přednost ostatní middleware pro obsluhu statických souborů, akcí MVC atd.
Sezení Poskytuje podporu pro správu uživatelských relací. Před komponentami, které vyžadují Session.
Statické soubory 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 nezbytné pro přijetí požadavků WebSocket.

Další materiály