Sdílet prostřednictvím


Směrování v ASP.NET Core

Ryan Nowak, Kirk Larkin a Rick Anderson

Poznámka:

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

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete v tomto článku ve 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í.

Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Směrování zodpovídá za porovnávání příchozích požadavků HTTP a odesílání těchto požadavků do spustitelných koncových bodů aplikace. Koncové body jsou jednotky spustitelného kódu zpracování požadavků. Koncové body se definují v aplikaci a konfigurují se při spuštění aplikace. Proces porovnávání koncových bodů může extrahovat hodnoty z adresy URL požadavku a poskytnout tyto hodnoty pro zpracování požadavků. Pomocí informací o koncovém bodu z aplikace je směrování také schopné generovat adresy URL, které se mapují na koncové body.

Aplikace můžou nakonfigurovat směrování pomocí:

Tento článek se zabývá podrobnostmi nízké úrovně směrování ASP.NET Core. Informace o konfiguraci směrování:

Základy směrování

Následující kód ukazuje základní příklad směrování:

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

app.MapGet("/", () => "Hello World!");

app.Run();

Předchozí příklad obsahuje jeden koncový bod pomocí MapGet metody:

  • Při odeslání požadavku HTTP GET na kořenovou adresu URL /:
    • Delegát požadavku se spustí.
    • Hello World! je zapsán do odpovědi HTTP.
  • Pokud metoda požadavku není GET nebo kořenová adresa URL není /, žádná trasa neodpovídá a vrátí se http 404.

Směrování používá dvojici middlewaru, zaregistrovaného uživatelem UseRouting a UseEndpoints:

  • UseRouting přidá trasu odpovídající kanálu middlewaru. Tento middleware se podívá na sadu koncových bodů definovaných v aplikaci a vybere nejlepší shodu na základě požadavku.
  • UseEndpoints přidá spuštění koncového bodu do kanálu middlewaru. Spustí delegáta přidruženého k vybranému koncovému bodu.

Aplikace obvykle nemusí volat UseRouting ani UseEndpoints. WebApplicationBuilder konfiguruje middlewarový kanál, který zabalí middleware přidaný do Program.cs UseRouting a UseEndpoints. Aplikace ale můžou změnit pořadí, ve kterém UseRouting se budou spouštět, UseEndpoints a to explicitním voláním těchto metod. Například následující kód provede explicitní volání UseRouting:

app.Use(async (context, next) =>
{
    // ...
    await next(context);
});

app.UseRouting();

app.MapGet("/", () => "Hello World!");

V předchozím kódu:

  • Volání pro app.Use registraci vlastního middlewaru, který se spouští na začátku kanálu.
  • Volání konfiguruje UseRouting middleware odpovídající směrování tak, aby se spustil po vlastním middlewaru.
  • Koncový bod zaregistrovaný na MapGet konci kanálu se spustí.

Pokud by předchozí příklad nezahrnoval volání UseRouting, vlastní middleware by se spustil za middlewarem odpovídajícím trasě.

Poznámka: Trasy přidané přímo ke WebApplication spuštění na konci kanálu.

Koncové body

Metoda MapGet se používá k definování koncového bodu. Koncový bod je něco, co může být:

  • Vybráno tak, že se shoduje s adresou URL a metodou HTTP.
  • Spustí se spuštěním delegáta.

Koncové body, které se dají spárovat a spouštět aplikací, jsou nakonfigurované v UseEndpoints. Například MapGet, MapPosta podobné metody připojit žádosti delegáty do systému směrování. Další metody lze použít k připojení funkcí architektury ASP.NET Core ke směrovacímu systému:

Následující příklad ukazuje směrování s sofistikovanější šablonou trasy:

app.MapGet("/hello/{name:alpha}", (string name) => $"Hello {name}!");

Řetězec /hello/{name:alpha} je šablona trasy. Ke konfiguraci shody koncového bodu se používá šablona trasy. V tomto případě šablona odpovídá:

  • Adresa URL, jako je /hello/Docs
  • Všechny cesty URL, které začínají /hello/ posloupností abecedních znaků. :alpha použije omezení trasy, které odpovídá pouze abecedním znakům. Omezení tras jsou vysvětlena dále v tomto článku.

Druhý segment cesty URL: {name:alpha}

Následující příklad ukazuje směrování s kontrolami stavu a autorizací:

app.UseAuthentication();
app.UseAuthorization();

app.MapHealthChecks("/healthz").RequireAuthorization();
app.MapGet("/", () => "Hello World!");

Předchozí příklad ukazuje, jak:

  • S směrováním je možné použít autorizační middleware.
  • Koncové body je možné použít ke konfiguraci chování autorizace.

Volání MapHealthChecks přidá koncový bod kontroly stavu. Při zřetězování RequireAuthorization k tomuto volání se ke koncovému bodu připojí zásady autorizace.

Volání UseAuthentication a UseAuthorization přidání middlewaru pro ověřování a autorizaci Tyto middleware jsou umístěny mezi UseRouting a UseEndpoints tak, aby mohly:

  • Podívejte se, který koncový bod vybral UseRouting.
  • Před odesláním do koncového bodu použijte zásadu UseEndpoints autorizace.

Metadata koncového bodu

V předchozím příkladu existují dva koncové body, ale k autorizační zásadě jsou připojené jenom koncové body kontroly stavu. Pokud požadavek odpovídá koncovému bodu kontroly stavu, /healthzprovede se kontrola autorizace. To ukazuje, že koncové body můžou mít připojená další data. Tato další data se nazývají metadata koncového bodu:

  • Metadata je možné zpracovat pomocí middlewaru pracujícího se směrováním.
  • Metadata můžou být libovolného typu .NET.

Koncepty směrování

Systém směrování vychází z kanálu middlewaru přidáním výkonného konceptu koncového bodu . Koncové body představují jednotky funkcí aplikace, které se vzájemně liší z hlediska směrování, autorizace a libovolného počtu systémů ASP.NET Core.

Definice koncového bodu ASP.NET Core

Koncový bod ASP.NET Core je:

Následující kód ukazuje, jak načíst a zkontrolovat koncový bod odpovídající aktuálnímu požadavku:

app.Use(async (context, next) =>
{
    var currentEndpoint = context.GetEndpoint();

    if (currentEndpoint is null)
    {
        await next(context);
        return;
    }

    Console.WriteLine($"Endpoint: {currentEndpoint.DisplayName}");

    if (currentEndpoint is RouteEndpoint routeEndpoint)
    {
        Console.WriteLine($"  - Route Pattern: {routeEndpoint.RoutePattern}");
    }

    foreach (var endpointMetadata in currentEndpoint.Metadata)
    {
        Console.WriteLine($"  - Metadata: {endpointMetadata}");
    }

    await next(context);
});

app.MapGet("/", () => "Inspect Endpoint.");

Koncový bod, pokud je vybraný, lze načíst z objektu HttpContext. Jeho vlastnosti je možné zkontrolovat. Objekty koncového bodu jsou neměnné a po vytvoření není možné je změnit. Nejběžnějším typem RouteEndpointkoncového bodu je . RouteEndpoint obsahuje informace, které mu umožní vybrat směrovací systém.

V předchozím kódu aplikace. Slouží ke konfiguraci vloženého middlewaru.

Následující kód ukazuje, že v závislosti na tom, kde app.Use se v kanálu volá, nemusí existovat koncový bod:

// Location 1: before routing runs, endpoint is always null here.
app.Use(async (context, next) =>
{
    Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

app.UseRouting();

// Location 2: after routing runs, endpoint will be non-null if routing found a match.
app.Use(async (context, next) =>
{
    Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

// Location 3: runs when this endpoint matches
app.MapGet("/", (HttpContext context) =>
{
    Console.WriteLine($"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    return "Hello World!";
}).WithDisplayName("Hello");

app.UseEndpoints(_ => { });

// Location 4: runs after UseEndpoints - will only run if there was no match.
app.Use(async (context, next) =>
{
    Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

Předchozí ukázka přidá Console.WriteLine příkazy, které zobrazují, jestli byl vybrán koncový bod nebo ne. Pro přehlednost ukázka přiřadí zadaný koncový bod zobrazovaný název / .

Předchozí ukázka také zahrnuje volání UseRouting a UseEndpoints řízení přesně toho, kdy se tyto middlewary spouští v rámci kanálu.

Spuštění tohoto kódu s adresou URL / zobrazení:

1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello

Spuštění tohoto kódu s jakoukoli jinou adresou URL zobrazí:

1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)

Tento výstup ukazuje, že:

  • Koncový bod je před zavolání vždy null UseRouting .
  • Pokud se najde shoda, koncový bod je mezi UseRouting a UseEndpoints.
  • Middleware UseEndpoints je terminál , když se najde shoda. Middleware terminálu je definován dále v tomto článku.
  • Middleware po spuštění pouze v UseEndpoints případě, že nebyla nalezena žádná shoda.

Middleware UseRouting používá metodu SetEndpoint pro připojení koncového bodu k aktuálnímu kontextu. Middleware je možné nahradit UseRouting vlastní logikou a přesto získat výhody používání koncových bodů. Koncové body jsou primitivní nízké úrovně, jako je middleware, a nejsou svázané s implementací směrování. Většina aplikací nemusí nahradit UseRouting vlastní logikou.

Middleware UseEndpoints je navržený tak, aby se používal společně s middlewarem UseRouting . Základní logika pro spuštění koncového bodu není složitá. Slouží GetEndpoint k načtení koncového bodu a následnému vyvolání jeho RequestDelegate vlastnosti.

Následující kód ukazuje, jak může middleware ovlivnit nebo reagovat na směrování:

app.UseHttpMethodOverride();
app.UseRouting();

app.Use(async (context, next) =>
{
    if (context.GetEndpoint()?.Metadata.GetMetadata<RequiresAuditAttribute>() is not null)
    {
        Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
    }

    await next(context);
});

app.MapGet("/", () => "Audit isn't required.");
app.MapGet("/sensitive", () => "Audit required for sensitive data.")
    .WithMetadata(new RequiresAuditAttribute());
public class RequiresAuditAttribute : Attribute { }

Předchozí příklad ukazuje dva důležité koncepty:

  • Middleware se může spustit před UseRouting úpravou dat, se kterými směrování pracuje.
  • Middleware se může spustit mezi UseRouting směrováním a UseEndpoints zpracovat výsledky směrování před spuštěním koncového bodu.
    • Middleware, který běží mezi UseRouting a UseEndpoints:
      • Obvykle kontroluje metadata, aby porozuměla koncovým bodům.
      • Často provádí rozhodnutí o zabezpečení, jak to dělá UseAuthorization a UseCors.
    • Kombinace middlewaru a metadat umožňuje konfigurovat zásady pro jednotlivé koncové body.

Předchozí kód ukazuje příklad vlastního middlewaru, který podporuje zásady pro jednotlivé koncové body. Middleware zapíše protokol auditu přístupu k citlivým datům do konzoly. Middleware je možné nakonfigurovat tak, aby auditoval koncový bod s RequiresAuditAttribute metadaty. Tato ukázka ukazuje způsob vyjádření výslovného souhlasu, kdy se auditují jenom koncové body označené jako citlivé. Tuto logiku je možné definovat obráceně, auditovat všechno, co není označené jako bezpečné, například. Systém metadat koncového bodu je flexibilní. Tato logika by mohla být navržena jakýmkoli způsobem, který vyhovuje případu použití.

Předchozí vzorový kód je určený k předvedení základních konceptů koncových bodů. Ukázka není určená pro produkční použití. Ucelenější verze middlewaru protokolu auditu:

  • Přihlaste se k souboru nebo databázi.
  • Uveďte podrobnosti, jako je uživatel, IP adresa, název citlivého koncového bodu a další.

Metadata zásad auditu jsou definována RequiresAuditAttribute jako Attribute jednodušší použití s architekturami založenými na třídách, jako jsou kontrolery a SignalR. Při použití trasy ke kódu:

  • Metadata jsou připojena pomocí rozhraní API tvůrce.
  • Architektury založené na třídách zahrnují všechny atributy odpovídající metody a třídy při vytváření koncových bodů.

Osvědčenými postupy pro typy metadat je definovat buď jako rozhraní, nebo atributy. Rozhraní a atributy umožňují opakované použití kódu. Systém metadat je flexibilní a neukládá žádná omezení.

Porovnání middlewaru terminálu se směrováním

Následující příklad ukazuje terminálový middleware i směrování:

// Approach 1: Terminal Middleware.
app.Use(async (context, next) =>
{
    if (context.Request.Path == "/")
    {
        await context.Response.WriteAsync("Terminal Middleware.");
        return;
    }

    await next(context);
});

app.UseRouting();

// Approach 2: Routing.
app.MapGet("/Routing", () => "Routing.");

Styl middlewaru zobrazeného s terminálovým middlewarem Approach 1: . Říká se tomu middleware terminálu, protože dělá odpovídající operaci:

  • Odpovídající operace v předchozí ukázce je Path == "/" pro middleware a Path == "/Routing" pro směrování.
  • Když je shoda úspěšná, spustí některé funkce a vrátí se místo vyvolání middlewaru next .

Říká se tomu middleware terminálu, protože ukončí hledání, spustí některé funkce a vrátí se.

Následující seznam porovnává middleware terminálu se směrováním:

  • Oba přístupy umožňují ukončení kanálu zpracování:
    • Middleware ukončí kanál tím, že se místo vyvolání next.
    • Koncové body jsou vždy terminálové.
  • Middleware terminálu umožňuje umístění middlewaru na libovolné místo v kanálu:
  • Middleware terminálu umožňuje libovolnému kódu určit, kdy middleware odpovídá:
    • Vlastní kód pro porovnávání tras může být podrobný a obtížně zapisuje správně.
    • Směrování poskytuje jednoduchá řešení pro typické aplikace. Většina aplikací nevyžaduje vlastní kód pro porovnávání tras.
  • Rozhraní koncových bodů s middlewarem, jako UseAuthorization je například a UseCors.
    • Použití middlewaru terminálu s UseAuthorization autorizačním systémem nebo UseCors vyžaduje ruční propojení se systémem autorizace.

Koncový bod definuje obojí:

  • Delegát na zpracování požadavků.
  • Kolekce libovolných metadat. Metadata se používají k implementaci průřezových aspektů na základě zásad a konfigurace připojených k jednotlivým koncovým bodům.

Terminálový middleware může být efektivní nástroj, ale může vyžadovat:

  • Značné množství kódování a testování.
  • Ruční integrace s jinými systémy za účelem dosažení požadované úrovně flexibility.

Před zápisem middlewaru terminálu zvažte integraci se směrováním.

Existující middleware terminálu, který se integruje s mapou , nebo MapWhen se obvykle dá převést na koncový bod podporující směrování. MapHealthChecks ukazuje vzor pro router-ware:

Následující kód ukazuje použití MapHealthChecks:

app.UseAuthentication();
app.UseAuthorization();

app.MapHealthChecks("/healthz").RequireAuthorization();

Předchozí ukázka ukazuje, proč je vrácení objektu tvůrce důležité. Vrácení objektu tvůrce umožňuje vývojáři aplikací konfigurovat zásady, jako je autorizace pro koncový bod. V tomto příkladu nemá middleware kontroly stavu žádnou přímou integraci se systémem autorizace.

Systém metadat byl vytvořen v reakci na problémy zjištěné autory rozšiřitelnosti pomocí middlewaru terminálu. Pro každý middleware je problematické implementovat vlastní integraci s autorizačním systémem.

Porovnávání adres URL

  • Je proces, kterým směrování odpovídá příchozímu požadavku na koncový bod.
  • Je založená na datech v cestě URL a hlavičkách.
  • Můžete ho rozšířit, abyste zvážili všechna data v požadavku.

Když se spustí middleware směrování, nastaví Endpoint hodnotu požadavku na funkci HttpContext požadavku z aktuálního požadavku:

Middleware, který se spustí po spuštění middlewaru směrování, může zkontrolovat koncový bod a provést akci. Například autorizační middleware může probrat kolekci metadat koncového bodu pro zásady autorizace. Po spuštění veškerého middlewaru v kanálu zpracování požadavků se vyvolá delegát vybraného koncového bodu.

Systém směrování ve směrování koncového bodu zodpovídá za veškerá rozhodnutí o odesílání. Vzhledem k tomu, že middleware používá zásady na základě vybraného koncového bodu, je důležité, aby:

  • Jakékoli rozhodnutí, které může ovlivnit odesílání nebo použití zásad zabezpečení, se provádí v rámci systému směrování.

Upozorňující

V případě zpětné kompatibility se při spuštění delegáta koncového RouteContext.RouteData bodu kontroleru nebo Razor stránky nastaví vlastnosti na odpovídající hodnoty na základě dosud provedeného zpracování požadavku.

Typ RouteContext bude v budoucí verzi označen jako zastaralý:

  • Migrovat RouteData.Values na HttpRequest.RouteValues.
  • Migrace RouteData.DataTokens pro načtení IDataTokensMetadata z metadat koncového bodu

Porovnávání adres URL funguje v konfigurovatelné sadě fází. V každé fázi je výstup sadou shod. Sadu shod lze dále zúžit v další fázi. Implementace směrování nezaručuje pořadí zpracování pro odpovídající koncové body. Všechny možné shody se zpracovávají najednou. Fáze porovnávání adres URL probíhají v následujícím pořadí. ASP.NET Core:

  1. Zpracovává cestu URL vůči sadě koncových bodů a jejich šablon tras a shromažďuje všechny shody.
  2. Vezme předchozí seznam a odebere shody, které selžou s použitými omezeními trasy.
  3. Vezme předchozí seznam a odebere shody, které sadu instancí selžou MatcherPolicy .
  4. EndpointSelector Použije k poslednímu rozhodnutí z předchozího seznamu.

Seznam koncových bodů má prioritu podle následujících:

Všechny odpovídající koncové body se zpracovávají v každé fázi, dokud se nedosáhne EndpointSelector . Jedná se EndpointSelector o konečnou fázi. Jako nejlepší shodu zvolí koncový bod s nejvyšší prioritou. Pokud existují jiné shody se stejnou prioritou jako nejlepší shoda, vyvolá se výjimka nejednoznačné shody.

Priorita trasy se vypočítá na základě konkrétnější šablony trasy, která má vyšší prioritu. Představte si například šablony /hello a /{message}:

  • Obě odpovídají cestě /helloURL .
  • /hello je konkrétnější a proto vyšší priorita.

Obecně platí, že priorita trasy má dobrou úlohu při výběru nejvhodnější shody pro druhy schémat adres URL používaných v praxi. Používejte Order pouze v případě potřeby, abyste se vyhnuli nejednoznačnosti.

Vzhledem k druhům rozšiřitelnosti poskytované směrováním není možné, aby směrovací systém předem počítaly nejednoznačné trasy. Představte si příklad, jako jsou šablony /{message:alpha} tras a /{message:int}:

  • Omezení alpha odpovídá pouze abecedním znakům.
  • Omezení int odpovídá pouze číslu.
  • Tyto šablony mají stejnou prioritu tras, ale obě adresy URL se neshodují.
  • Pokud systém směrování nahlásil při spuštění nejednoznačnosti, zablokoval by tento platný případ použití.

Upozorňující

Pořadí operací uvnitř UseEndpoints nemá vliv na chování směrování, s jednou výjimkou. MapControllerRoute a MapAreaRoute automaticky přiřadí hodnotu objednávky ke svým koncovým bodům na základě pořadí, ve které jsou vyvolány. To simuluje dlouhodobé chování kontrolerů bez systému směrování, který poskytuje stejné záruky jako starší implementace směrování.

Směrování koncových bodů v ASP.NET Core:

  • Nemá koncept tras.
  • Neposkytuje záruky objednávání. Všechny koncové body se zpracovávají najednou.

Priorita šablony trasy a pořadí výběru koncového bodu

Priorita šablony trasy je systém, který každé šabloně trasy přiřadí hodnotu na základě toho, jak je konkrétní. Priorita šablony trasy:

  • Vyhne se nutnosti upravovat pořadí koncových bodů v běžných případech.
  • Snaží se shodovat s očekáváními obecného chování směrování.

Představte si například šablony /Products/List a /Products/{id}. Bylo by rozumné předpokládat, že /Products/List je lepší shoda než /Products/{id} pro cestu /Products/ListURL . To funguje, protože literálový segment /List je považován za lepší prioritu než segment /{id}parametru .

Podrobnosti o tom, jak funguje priorita, jsou svázány s tím, jak se definují šablony tras:

  • Šablony s více segmenty jsou považovány za konkrétnější.
  • Segment s literálovým textem se považuje za konkrétnější než segment parametru.
  • Segment parametru s omezením se považuje za konkrétnější než jeden bez.
  • Složitý segment se považuje za specifický jako segment parametru s omezením.
  • Parametry pro zachytávání jsou nejmíň specifické. Důležité informace o trasách pro zachytávání všech tras najdete v části Šablony tras catch-all.

Koncepty generování adres URL

Generování adres URL:

  • Je proces, pomocí kterého směrování může vytvořit cestu URL na základě sady hodnot tras.
  • Umožňuje logické oddělení mezi koncovými body a adresami URL, které k nim přistupují.

Směrování koncových bodů zahrnuje LinkGenerator rozhraní API. LinkGenerator je jednoúčelová služba, která je k dispozici z DI. Rozhraní LinkGenerator API se dá použít mimo kontext spuštěného požadavku. Mvc.IUrlHelper a scénáře, které se spoléhají , IUrlHelperjako jsou pomocné rutiny značek, pomocné rutiny HTML a výsledky akcí, používají LinkGenerator rozhraní API interně k poskytování možností generování odkazů.

Generátor odkazů je podporován konceptem adresního a adresního schématu. Schéma adres je způsob, jak určit koncové body, které by se měly zvážit pro generování propojení. Například scénáře názvu trasy a směrovacích hodnot, které mnoho uživatelů znáte z kontrolerů a Razor Stránky se implementují jako schéma adres.

Generátor odkazů může odkazovat na kontrolery a Razor stránky pomocí následujících rozšiřujících metod:

Přetížení těchto metod přijímají argumenty, které zahrnují HttpContext. Tyto metody jsou funkčně ekvivalentní url.Action a Url.Page, ale nabízejí další flexibilitu a možnosti.

Metody GetPath* jsou nejvíce podobné Url.Action a Url.Pagev tom, že vygenerují identifikátor URI obsahující absolutní cestu. Metody GetUri* vždy generují absolutní identifikátor URI obsahující schéma a hostitele. Metody, které přijímají HttpContext vygenerování identifikátoru URI v kontextu prováděcího požadavku. Hodnoty okolní trasy, základní cesta URL, schéma a hostitel ze spuštěného požadavku se použijí, pokud je nepřepíšete.

LinkGenerator je volána s adresou. Generování identifikátoru URI probíhá ve dvou krocích:

  1. Adresa je svázaná se seznamem koncových bodů, které odpovídají adrese.
  2. Vyhodnocuje se RoutePattern každý koncový bod, dokud se nenajde vzor trasy, který odpovídá zadaným hodnotám. Výsledný výstup se zkombinuje s ostatními částmi identifikátoru URI zadanými do generátoru propojení a vrátí se.

Metody poskytované standardními možnostmi LinkGenerator generování propojení pro libovolný typ adresy. Nejpohodlnější způsob použití generátoru propojení je prostřednictvím rozšiřujících metod, které provádějí operace pro konkrétní typ adresy:

Extension – metoda Popis
GetPathByAddress Vygeneruje identifikátor URI s absolutní cestou na základě zadaných hodnot.
GetUriByAddress Vygeneruje absolutní identifikátor URI na základě zadaných hodnot.

Upozorňující

Věnujte pozornost následujícím důsledkům volání LinkGenerator metod:

  • V konfiguraci aplikace používejte GetUri* metody rozšíření s opatrností, která neověřuje hlavičku Host příchozích požadavků. Pokud hlavička Host příchozích požadavků není ověřená, může být nedůvěryhodný vstup požadavku odeslán zpět klientovi v identifikátorech URI v zobrazení nebo stránce. Doporučujeme, aby všechny produkční aplikace nakonfigurovaly svůj server tak, aby ověřovaly hlavičku Host proti známým platným hodnotám.

  • Používejte LinkGenerator s opatrností v middlewaru v kombinaci s Map nebo MapWhen. Map* změní základní cestu prováděcího požadavku, která má vliv na výstup generování propojení. LinkGenerator Všechna rozhraní API umožňují zadat základní cestu. Zadejte prázdnou Map* základní cestu, která vrátí zpět vliv na generování propojení.

Příklad middlewaru

V následujícím příkladu middleware používá LinkGenerator rozhraní API k vytvoření odkazu na metodu akce, která obsahuje seznam produktů pro ukládání. Použití generátoru odkazů vložením do třídy a volání GenerateLink je k dispozici pro libovolnou třídu v aplikaci:

public class ProductsMiddleware
{
    private readonly LinkGenerator _linkGenerator;

    public ProductsMiddleware(RequestDelegate next, LinkGenerator linkGenerator) =>
        _linkGenerator = linkGenerator;

    public async Task InvokeAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Plain;

        var productsPath = _linkGenerator.GetPathByAction("Products", "Store");

        await httpContext.Response.WriteAsync(
            $"Go to {productsPath} to see our products.");
    }
}

Šablony tras

Tokeny v rámci {} definují parametry trasy, které jsou vázané, pokud se trasa shoduje. V segmentu trasy je možné definovat více než jeden parametr trasy, ale parametry trasy musí být oddělené hodnotou literálu. Příklad:

{controller=Home}{action=Index}

není platná trasa, protože neexistuje žádná hodnota literálu mezi {controller} a {action}. Parametry trasy musí mít název a mohou mít zadané další atributy.

Literál jiný než směrovací parametry (například {id}) a oddělovač / cesty musí odpovídat textu v adrese URL. Porovnávání textu nerozlišuje malá a velká písmena a vychází z dekódované reprezentace cesty adresy URL. Chcete-li se shodovat s oddělovačem { parametru trasy literálu nebo }, uchytáte oddělovač opakováním znaku. Například {{ nebo }}.

Hvězdička * nebo dvojitá hvězdička **:

  • Lze použít jako předponu parametru trasy pro vazbu na rest identifikátor URI.
  • Nazývají se parametry catch-all. Příklad: blog/{**slug}
    • Odpovídá libovolnému identifikátoru blog/ URI, který začíná a má za ním libovolnou hodnotu.
    • Následující hodnota blog/ je přiřazena ke slug směrovací hodnotě.

Upozorňující

Parametr catch-all může nesprávně odpovídat trasám kvůli chybě při směrování. Aplikace ovlivněné touto chybou mají následující charakteristiky:

  • Například trasa pro zachytávání– vše {**slug}"
  • Trasa catch-all neodpovídá požadavkům, které by se měly shodovat.
  • Odebráním jiných tras začne fungovat zachytávání všech tras.

Podívejte se na chyby GitHubu 18677 a 16579 , například případy, které tuto chybu narazily.

Oprava výslovného souhlasu s touto chybou je obsažená v sadě .NET Core 3.1.301 SDK a novějších verzích. Následující kód nastaví interní přepínač, který tuto chybu opraví:

public static void Main(string[] args)
{
   AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", 
                         true);
   CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.

Parametry catch-all mohou také odpovídat prázdnému řetězci.

Parametr catch-all umisťuje příslušné znaky při použití trasy k vygenerování adresy URL, včetně znaků oddělovače / cest. Například trasa foo/{*path} s hodnotami { path = "my/path" } trasy generuje foo/my%2Fpath. Všimněte si řídicího lomítka. Pokud chcete znaky oddělovače cest odezvy použít předponu parametru ** trasy. Trasa foo/{**path} s { path = "my/path" } vygenerovanými .foo/my/path

Vzory adres URL, které se pokoušejí zachytit název souboru s volitelnou příponou souboru, mají další důležité informace. Představte si například šablonu files/{filename}.{ext?}. Když hodnoty obou filename hodnot existují ext , naplní se obě hodnoty. Pokud v adrese URL existuje jenom hodnota filename , trasa se shoduje, protože koncové hodnoty . jsou volitelné. Následující adresy URL odpovídají této trase:

  • /files/myFile.txt
  • /files/myFile

Parametry trasy můžou mít výchozí hodnoty určené zadáním výchozí hodnoty za názvem parametru odděleným symbolem rovná se (=). Například {controller=Home} definuje Home jako výchozí hodnotu pro controller. Výchozí hodnota se použije, pokud v adrese URL parametru není žádná hodnota. Parametry trasy jsou volitelné připojením otazníku (?) na konec názvu parametru. Například id?. Rozdíl mezi volitelnými hodnotami a výchozími parametry trasy je:

  • Parametr trasy s výchozí hodnotou vždy vytvoří hodnotu.
  • Volitelný parametr má hodnotu pouze v případě, že je hodnota poskytována adresou URL požadavku.

Parametry trasy můžou mít omezení, která musí odpovídat hodnotě trasy vázané z adresy URL. Přidání : a omezení názvu za názvem parametru trasy určuje vložené omezení parametru trasy. Pokud omezení vyžaduje argumenty, jsou za názvem omezení uzavřeny v závorkách (...) . Více vložených omezení lze zadat připojením jiného : názvu a názvu omezení.

Název omezení a argumenty se předávají službě IInlineConstraintResolver , aby se vytvořila instance IRouteConstraint , která se má použít při zpracování adresy URL. Například šablona blog/{article:minlength(10)} trasy určuje minlength omezení s argumentem 10. Další informace o omezeních tras a seznamu omezení poskytovaných architekturou najdete v části Omezení trasy.

Parametry trasy mohou mít také transformátory parametrů. Transformátory parametrů transformují hodnotu parametru při generování odkazů a odpovídajících akcí a stránek na adresy URL. Stejně jako omezení lze transformátory parametrů přidat do parametru trasy přidáním : názvu a transformátoru za název parametru trasy. Například šablona blog/{article:slugify} trasy určuje slugify transformátor. Další informace o transformátorech parametrů naleznete v části Transformátory parametrů .

Následující tabulka ukazuje ukázkové šablony tras a jejich chování:

Šablona trasy Příklad odpovídajícího identifikátoru URI Identifikátor URI požadavku...
hello /hello Odpovídá pouze jedné cestě /hello.
{Page=Home} / Odpovídá a nastaví Page na Home.
{Page=Home} /Contact Odpovídá a nastaví Page na Contact.
{controller}/{action}/{id?} /Products/List Mapuje se na Products kontroler a List akci.
{controller}/{action}/{id?} /Products/Details/123 Mapuje se Products na kontroler a Details akci s nastavenouid na hodnotu 123.
{controller=Home}/{action=Index}/{id?} / Mapuje se na kontroler a Index metoduHome. Vlastnost id je ignorována.
{controller=Home}/{action=Index}/{id?} /Products Mapuje se na kontroler a Index metoduProducts. Vlastnost id je ignorována.

Použití šablony je obecně nejjednodušším přístupem ke směrování. Omezení a výchozí hodnoty lze zadat také mimo šablonu trasy.

Komplexní segmenty

Komplexní segmenty se zpracovávají pomocí odpovídajících oddělovačů literálů zprava doleva způsobem, který není greedy . Jedná se například [Route("/a{b}c{d}")] o složitý segment. Složité segmenty fungují určitým způsobem, který je potřeba pochopit, aby je bylo možné úspěšně použít. Příklad v této části ukazuje, proč složité segmenty skutečně fungují dobře, když se text oddělovače nezobrazí uvnitř hodnot parametrů. Použití regulárního výrazu a ruční extrahování hodnot je potřeba pro složitější případy.

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Toto je souhrn kroků, které směrování provádí pomocí šablony /a{b}c{d} a cesty /abcdURL . Slouží | k vizualizaci fungování algoritmu:

  • První literál, zprava doleva, je c. Hledá se tedy /abcd zprava a najde /ab|c|d.
  • Vše napravo (d) se teď shoduje s parametrem {d}trasy .
  • Další literál, zprava doleva, je a. Takže /ab|c|d je prohledáno od místa, kde jsme skončili, pak a je nalezen /|a|b|c|d.
  • Hodnota vpravo (b) se teď shoduje s parametrem {b}trasy .
  • Neexistuje žádný zbývající text a žádná zbývající šablona trasy, takže se jedná o shodu.

Tady je příklad záporného případu, který používá stejnou šablonu /a{b}c{d} a cestu /aabcdURL . Slouží | k vizualizaci fungování algoritmu. Tento případ není shoda, která je vysvětlená stejným algoritmem:

  • První literál, zprava doleva, je c. Hledá se tedy /aabcd zprava a najde /aab|c|d.
  • Vše napravo (d) se teď shoduje s parametrem {d}trasy .
  • Další literál, zprava doleva, je a. Takže /aab|c|d je prohledáno od místa, kde jsme skončili, pak a je nalezen /a|a|b|c|d.
  • Hodnota vpravo (b) se teď shoduje s parametrem {b}trasy .
  • V tomto okamžiku existuje zbývající text a, ale algoritmus vyčeráhl šablonu trasy, aby parsovala, takže se nejedná o shodu.

Vzhledem k tomu, že odpovídající algoritmus není greedy:

  • Odpovídá nejmenšímu množství textu, který je možné v každém kroku.
  • Případ, kdy se hodnota oddělovače zobrazí uvnitř hodnot parametrů, způsobí, že se neshoduje.

Regulární výrazy poskytují mnohem větší kontrolu nad jejich odpovídajícím chováním.

Porovnávání greedy, označované také jako maximální počet pokusů o nalezení nejdelší možné shody ve vstupním textu, který splňuje vzor regulárního výrazu. Ne greedy párování, označované také jako opožděné párování, hledá nejkratší možnou shodu ve vstupním textu, který splňuje vzor regulárního výrazu.

Směrování se speciálními znaky

Směrování se speciálními znaky může vést k neočekávaným výsledkům. Představte si například kontroler s následující metodou akce:

[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null || todoItem.Name == null)
    {
        return NotFound();
    }

    return todoItem.Name;
}

Pokud string id obsahuje následující kódované hodnoty, mohou dojít k neočekávaným výsledkům:

ASCII Encoded
/ %2F
+

Parametry trasy nejsou vždy dekódovány adresou URL. Tento problém může být vyřešen v budoucnu. Další informace najdete v tomto problému na GitHubu.

Omezení trasy

Omezení směrování se provádějí, když došlo ke shodě s příchozí adresou URL a cesta url se tokenizuje do hodnot tras. Omezení tras obecně kontrolují hodnotu trasy přidruženou prostřednictvím šablony trasy a ověřte, jestli je hodnota přijatelná, nebo nepravdivá. Některá omezení trasy používají data mimo hodnotu trasy a zvažují, jestli je možné požadavek směrovat. Například HttpMethodRouteConstraint může přijmout nebo odmítnout požadavek na základě jeho příkazu HTTP. Omezení se používají v požadavcích směrování a generování propojení.

Upozorňující

Nepoužívejte omezení pro ověřování vstupu. Pokud se pro ověření vstupu používají omezení, výsledkem neplatného 404 vstupu je odpověď Nenalezena. Neplatný vstup by měl vytvořit chybný 400 požadavek s příslušnou chybovou zprávou. Omezení tras se používají k nejednoznačnosti podobných tras, nikoli k ověření vstupů pro konkrétní trasu.

Následující tabulka ukazuje ukázková omezení směrování a jejich očekávané chování:

omezení Příklad Příklady shod Notes
int {id:int} 123456789, -123456789 Odpovídá libovolnému celočíselnému číslu.
bool {active:bool} true, FALSE Shody true nebo false. Nerozlišují se malá a velká písmena
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Odpovídá platné DateTime hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
decimal {price:decimal} 49.99, -1,000.01 Odpovídá platné decimal hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
double {weight:double} 1.234, -1,001.01e8 Odpovídá platné double hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
float {weight:float} 1.234, -1,001.01e8 Odpovídá platné float hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Odpovídá platné Guid hodnotě.
long {ticks:long} 123456789, -123456789 Odpovídá platné long hodnotě.
minlength(value) {username:minlength(4)} Rick Řetězec musí mít alespoň 4 znaky.
maxlength(value) {filename:maxlength(8)} MyFile Řetězec nesmí být delší než 8 znaků.
length(length) {filename:length(12)} somefile.txt Řetězec musí mít přesně 12 znaků.
length(min,max) {filename:length(8,16)} somefile.txt Řetězec musí mít maximálně 8 znaků a nesmí být delší než 16 znaků.
min(value) {age:min(18)} 19 Celočíselná hodnota musí být alespoň 18.
max(value) {age:max(120)} 91 Celočíselná hodnota nesmí být větší než 120.
range(min,max) {age:range(18,120)} 91 Celočíselná hodnota musí být alespoň 18, ale nesmí být větší než 120.
alpha {name:alpha} Rick Řetězec musí obsahovat jeden nebo více abecedních znaků a-z a nerozlišuje velká a malá písmena.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 Řetězec musí odpovídat regulárnímu výrazu. Podívejte se na tipy k definování regulárního výrazu.
required {name:required} Rick Používá se k vynucení toho, aby během generování adresy URL byla přítomna hodnota, která není parametrem.

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Pro jeden parametr lze použít více omezení oddělených dvojtečkami. Například následující omezení omezuje parametr na celočíselnou hodnotu 1 nebo vyšší:

[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }

Upozorňující

Omezení směrování, která ověřují adresu URL a jsou převedeny na typ CLR, vždy používají neutrální jazykovou verzi. Například převod na typ int CLR nebo DateTime. Tato omezení předpokládají, že adresa URL není lokalizovatelná. Omezení tras poskytovaná architekturou nemění hodnoty uložené v hodnotách tras. Všechny hodnoty směrování parsované z adresy URL se ukládají jako řetězce. Omezení se například pokusí převést hodnotu trasy na hodnotu float, ale převedená hodnota se použije pouze k ověření, float že se dá převést na plovoucí hodnotu.

Regulární výrazy v omezeních

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Regulární výrazy lze zadat jako vložená omezení pomocí regex(...) omezení trasy. Metody v rodině MapControllerRoute také přijímají literál objektu omezení. Pokud se tento formulář použije, řetězcové hodnoty se interpretují jako regulární výrazy.

Následující kód používá omezení vložených regulárních výrazů:

app.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
    () => "Inline Regex Constraint Matched");

Následující kód používá literál objektu k určení omezení regulárního výrazu:

app.MapControllerRoute(
    name: "people",
    pattern: "people/{ssn}",
    constraints: new { ssn = "^\\d{3}-\\d{2}-\\d{4}$", },
    defaults: new { controller = "People", action = "List" });

Architektura ASP.NET Core přidává RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant do konstruktoru regulárních výrazů. Podívejte RegexOptions se na popis těchto členů.

Regulární výrazy používají oddělovače a tokeny podobné těm používaným směrováním a jazykem C#. Tokeny regulárních výrazů musí být uchycené. Chcete-li použít regulární výraz ^\d{3}-\d{2}-\d{4}$ v vložené omezení, použijte jednu z následujících možností:

  • Nahraďte \ znaky zadané v řetězci jako \\ znaky ve zdrojovém souboru jazyka C#, aby bylo možné řídicí znak řetězce utéct \ .
  • Doslovné řetězcové literály.

Chcete-li uvozovat znaky {oddělovače parametrů směrování , [}]zdvojnásobit znaky ve výrazu, {{například , }}, , . ]][[ Následující tabulka ukazuje regulární výraz a jeho řídicí verzi:

Regulární výraz Řídicí regulární výraz
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Regulární výrazy používané při směrování často začínají znakem ^ a odpovídají počáteční pozici řetězce. Výrazy často končí znakem $ a odpovídají konci řetězce. Znaky ^ a $ zajistěte, aby regulární výraz odpovídal celé hodnotě parametru trasy. ^ Bez znaků a $ znaků regulární výraz odpovídá jakémukoli podřetězci v řetězci, což je často nežádoucí. Následující tabulka obsahuje příklady a vysvětluje, proč se shodují nebo se neshodují:

Výraz String Párování Komentář
[a-z]{2} dobrý den Ano Shoda podřetěžce
[a-z]{2} 123abc456 Ano Shoda podřetěžce
[a-z]{2} mz Ano Odpovídá výrazu
[a-z]{2} MZ Ano Nerozlišuje se malá a velká písmena.
^[a-z]{2}$ dobrý den No Viz ^ a $ výše
^[a-z]{2}$ 123abc456 No Viz ^ a $ výše

Další informace o syntaxi regulárních výrazů naleznete v tématu Regulární výrazy rozhraní .NET Framework.

Chcete-li omezit parametr na známou sadu možných hodnot, použijte regulární výraz. Například {action:regex(^(list|get|create)$)} odpovídá pouze hodnotě action trasy do list, getnebo create. Pokud je řetězec předán do slovníku omezení, je řetězec ^(list|get|create)$ ekvivalentní. Omezení, která se předávají ve slovníku omezení, která neodpovídají některému ze známých omezení, se také považují za regulární výrazy. Omezení předaná v šabloně, která neodpovídají některému ze známých omezení, se považují za regulární výrazy.

Vlastní omezení trasy

Vlastní omezení trasy je možné vytvořit implementací IRouteConstraint rozhraní. Rozhraní IRouteConstraint obsahuje Match, který vrátí true , pokud je omezení splněno a false jinak.

Vlastní omezení trasy jsou zřídka nutná. Před implementací vlastního omezení trasy zvažte alternativy, jako je například vazba modelu.

Složka ASP.NET Core Constraints poskytuje dobré příklady vytváření omezení. Například GuidRouteConstraint.

Pokud chcete použít vlastní IRouteConstrainttyp omezení trasy, musí být zaregistrovaný v kontejneru služby v aplikaci ConstraintMap . A ConstraintMap je slovník, který mapuje klíče omezení směrování na IRouteConstraint implementace, které tato omezení ověřují. Aplikaci ConstraintMap je možné aktualizovat buď Program.cs v rámci AddRouting hovoru, nebo konfigurací RouteOptions přímo pomocí builder.Services.Configure<RouteOptions>. Příklad:

builder.Services.AddRouting(options =>
    options.ConstraintMap.Add("noZeroes", typeof(NoZeroesRouteConstraint)));

Předchozí omezení se použije v následujícím kódu:

[ApiController]
[Route("api/[controller]")]
public class NoZeroesController : ControllerBase
{
    [HttpGet("{id:noZeroes}")]
    public IActionResult Get(string id) =>
        Content(id);
}

Implementace NoZeroesRouteConstraint brání 0 použití v parametru trasy:

public class NoZeroesRouteConstraint : IRouteConstraint
{
    private static readonly Regex _regex = new(
        @"^[1-9]*$",
        RegexOptions.CultureInvariant | RegexOptions.IgnoreCase,
        TimeSpan.FromMilliseconds(100));

    public bool Match(
        HttpContext? httpContext, IRouter? route, string routeKey,
        RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (!values.TryGetValue(routeKey, out var routeValue))
        {
            return false;
        }

        var routeValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);

        if (routeValueString is null)
        {
            return false;
        }

        return _regex.IsMatch(routeValueString);
    }
}

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Předchozí kód:

  • Zabraňuje 0 v {id} segmentu trasy.
  • Ukazuje se, že poskytuje základní příklad implementace vlastního omezení. Neměla by se používat v produkční aplikaci.

Následující kód je lepším přístupem k tomu, aby se zabránilo zpracování obsahujícího id 0 :

[HttpGet("{id}")]
public IActionResult Get(string id)
{
    if (id.Contains('0'))
    {
        return StatusCode(StatusCodes.Status406NotAcceptable);
    }

    return Content(id);
}

Předchozí kód má oproti přístupu následující výhody NoZeroesRouteConstraint :

  • Nevyžaduje vlastní omezení.
  • Vrátí popisnější chybu, pokud parametr trasy obsahuje 0.

Transformátory parametrů

Transformátory parametrů:

Například vlastní slugify transformátor parametru ve vzoru blog\{article:slugify} trasy s Url.Action(new { article = "MyTestArticle" }) vygenerovanými blog\my-test-article.

Zvažte následující IOutboundParameterTransformer implementaci:

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string? TransformOutbound(object? value)
    {
        if (value is null)
        {
            return null;
        }

        return Regex.Replace(
            value.ToString()!,
                "([a-z])([A-Z])",
            "$1-$2",
            RegexOptions.CultureInvariant,
            TimeSpan.FromMilliseconds(100))
            .ToLowerInvariant();
    }
}

Chcete-li použít transformátor parametrů ve vzoru trasy, nakonfigurujte ho pomocí :ConstraintMap Program.cs

builder.Services.AddRouting(options =>
    options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer));

Architektura ASP.NET Core používá transformátory parametrů k transformaci identifikátoru URI, kde se koncový bod překládá. Například transformátory parametrů transformují směrovací hodnoty použité ke shodě area, controller, actiona page:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");

U předchozí šablony trasy se akce SubscriptionManagementController.GetAll shoduje s identifikátorem URI /subscription-management/get-all. Transformátor parametrů nemění směrovací hodnoty použité k vygenerování propojení. Například Url.Action("GetAll", "SubscriptionManagement") výstupy /subscription-management/get-all.

ASP.NET Core poskytuje konvence rozhraní API pro použití transformátorů parametrů se generovanými trasami:

Referenční informace ke generování adres URL

Tato část obsahuje odkaz na algoritmus implementovaný generováním adres URL. V praxi používají nejsložitější příklady generování adres URL kontrolery nebo Razor stránky. Další informace najdete v tématu Směrování v kontrolerů .

Proces generování adresy URL začíná voláním LinkGenerator.GetPathByAddress nebo podobnou metodou. Metoda je poskytována s adresou, sadou hodnot tras a volitelně informace o aktuálním požadavku z HttpContext.

Prvním krokem je použití adresy k vyřešení sady kandidátských koncových bodů pomocí typu IEndpointAddressScheme<TAddress> adresy.

Jakmile sada kandidátů najde schéma adres, koncové body se objednávají a zpracovávají iterativním způsobem, dokud operace generování adresy URL nebude úspěšná. Generování adresy URL nekontroluje nejednoznačnosti, první vrácený výsledek je konečný výsledek.

Řešení potíží s generováním adres URL pomocí protokolování

Prvním krokem při generování adresy URL při řešení potíží je nastavení úrovně Microsoft.AspNetCore.Routing protokolování na TRACE. LinkGenerator zaznamenává mnoho podrobností o jeho zpracování, které může být užitečné při řešení problémů.

Podrobnosti o generování adres URL najdete v referenčních informacích ke generování adres URL.

Adresy

Adresy jsou konceptem generování adres URL, které slouží k vytvoření vazby volání do generátoru odkazů na sadu kandidátských koncových bodů.

Adresy jsou rozšiřitelný koncept, který ve výchozím nastavení obsahuje dvě implementace:

  • Jako adresu použijte název koncového bodu (string):
    • Poskytuje podobné funkce jako název trasy MVC.
    • IEndpointNameMetadata Používá typ metadat.
    • Vyřeší zadaný řetězec s metadaty všech registrovaných koncových bodů.
    • Vyvolá výjimku při spuštění, pokud více koncových bodů používá stejný název.
    • Doporučuje se pro obecné použití mimo kontrolery a Razor stránky.
  • Použití hodnot směrování (RouteValuesAddress) jako adresy:
    • Poskytuje podobné funkce jako kontrolery a Razor starší generace adres URL stránky.
    • Velmi složité rozšířit a ladit.
    • Poskytuje implementaci, kterou IUrlHelperpoužívají pomocné rutiny značek, pomocné rutiny HTML, výsledky akcí atd.

Role schématu adres spočívá v přidružení mezi adresou a odpovídajícími koncovými body podle libovolných kritérií:

  • Schéma názvu koncového bodu provádí základní vyhledávání slovníku.
  • Schéma hodnot tras má komplexní nejlepší podmnožinu algoritmu sady.

Okolní hodnoty a explicitní hodnoty

Z aktuálního požadavku směrování přistupuje ke směrovacím hodnotám aktuálního požadavku HttpContext.Request.RouteValues. Hodnoty přidružené k aktuálnímu požadavku se označují jako okolní hodnoty. Pro účely srozumitelnosti se dokumentace týká směrovacích hodnot předávaných metodám jako explicitních hodnot.

Následující příklad ukazuje okolní hodnoty a explicitní hodnoty. Poskytuje okolní hodnoty z aktuálního požadavku a explicitních hodnot:

public class WidgetController : ControllerBase
{
    private readonly LinkGenerator _linkGenerator;

    public WidgetController(LinkGenerator linkGenerator) =>
        _linkGenerator = linkGenerator;

    public IActionResult Index()
    {
        var indexPath = _linkGenerator.GetPathByAction(
            HttpContext, values: new { id = 17 })!;

        return Content(indexPath);
    }

    // ...

Předchozí kód:

Následující kód poskytuje pouze explicitní hodnoty a žádné okolní hodnoty:

var subscribePath = _linkGenerator.GetPathByAction(
    "Subscribe", "Home", new { id = 17 })!;

Předchozí metoda vrátí /Home/Subscribe/17

Následující kód ve návratu WidgetController /Widget/Subscribe/17:

var subscribePath = _linkGenerator.GetPathByAction(
    HttpContext, "Subscribe", null, new { id = 17 });

Následující kód poskytuje kontroler z okolních hodnot v aktuálním požadavku a explicitní hodnoty:

public class GadgetController : ControllerBase
{
    public IActionResult Index() =>
        Content(Url.Action("Edit", new { id = 17 })!);
}

V předchozím kódu:

  • /Gadget/Edit/17 je vrácena.
  • UrlIUrlHelperzíská .
  • Action vygeneruje adresu URL s absolutní cestou pro metodu akce. Adresa URL obsahuje zadaný action název a route hodnoty.

Následující kód poskytuje okolní hodnoty z aktuálního požadavku a explicitní hodnoty:

public class IndexModel : PageModel
{
    public void OnGet()
    {
        var editUrl = Url.Page("./Edit", new { id = 17 });

        // ...
    }
}

Předchozí kód se nastaví url , když /Edit/17 Edit Razor Page obsahuje následující direktivu stránky:

@page "{id:int}"

Pokud stránka Upravit neobsahuje "{id:int}" šablonu trasy, url je /Edit?id=17.

Chování MVC IUrlHelper přidává kromě zde popsaných pravidel vrstvu složitosti:

  • IUrlHelper vždy poskytuje směrovací hodnoty z aktuálního požadavku jako okolní hodnoty.
  • IUrlHelper.Action vždy zkopíruje aktuální action hodnoty a controller směrovací hodnoty jako explicitní hodnoty, pokud je nepřepíše vývojář.
  • IUrlHelper.Page vždy zkopíruje aktuální page hodnotu trasy jako explicitní hodnotu, pokud ji nepřepíšete.
  • IUrlHelper.Page vždy přepíše aktuální handler hodnotu null trasy jako explicitní hodnoty, pokud ji nepřepíšete.

Uživatelé jsou často překvapeni chováním okolních hodnot, protože MVC zdánlivě nedodržuje vlastní pravidla. Z historických důvodů a z důvodu kompatibility mají určité hodnoty tras, jako actionje , controller, pagea handler mají vlastní zvláštní chování.

Ekvivalentní funkce poskytované LinkGenerator.GetPathByAction a LinkGenerator.GetPathByPage duplikuje tyto anomálie z důvodu kompatibility IUrlHelper .

Proces generování adres URL

Jakmile se najde sada kandidátských koncových bodů, algoritmus generování adres URL:

  • Zpracovává koncové body iterativním způsobem.
  • Vrátí první úspěšný výsledek.

Prvním krokem v tomto procesu je zneplatnění hodnoty trasy. Zneplatnění hodnoty trasy je proces, kterým směrování rozhodne, které hodnoty trasy z okolních hodnot se mají použít a které by se měly ignorovat. Každá okolní hodnota se považuje a buď v kombinaci s explicitními hodnotami, nebo je ignorována.

Nejlepším způsobem, jak přemýšlet o roli okolních hodnot, je, že se snaží uložit vývojáři aplikací psaní, v některých běžných případech. Scénáře, ve kterých jsou okolní hodnoty užitečné, se tradičně vztahují k MVC:

  • Při propojení s jinou akcí ve stejném řadiči není nutné zadat název kontroleru.
  • Při propojení s jiným řadičem ve stejné oblasti není nutné zadat název oblasti.
  • Při propojení se stejnou metodou akce není nutné zadávat hodnoty tras.
  • Při propojení s jinou částí aplikace nechcete přenášet hodnoty tras, které nemají v této části aplikace žádný význam.

Volání nebo LinkGenerator IUrlHelper vrácení jsou obvykle způsobená tím, že null nerozumí neplatné hodnotě trasy. Při řešení potíží s neplatnou hodnotou trasy můžete explicitně zadat více hodnot tras, abyste zjistili, jestli se tím problém vyřeší.

Neplatná hodnota trasy funguje na předpokladu, že schéma adresy URL aplikace je hierarchické s hierarchií vytvořenou zleva doprava. Představte si šablonu {controller}/{action}/{id?} směrování základního kontroleru, abyste získali intuitivní představu o tom, jak to funguje v praxi. Změna hodnoty zneplatní všechny směrovací hodnoty, které se zobrazují vpravo. To odráží předpoklad o hierarchii. Pokud má aplikace okolní hodnotu a idoperace určuje jinou hodnotu pro controller:

  • id nebude znovu použito, protože {controller} je nalevo od {id?}.

Několik příkladů demonstrujících tento princip:

  • Pokud explicitní hodnoty obsahují hodnotu pro id, okolní hodnota je id ignorována. Okolní hodnoty a controller action lze je použít.
  • Pokud explicitní hodnoty obsahují hodnotu pro action, je ignorována jakákoli okolní hodnota pro action . Lze použít okolní hodnoty controller . Pokud se explicitní hodnota pro jinou než okolní hodnota action actionpro , id hodnota se nepoužije. Pokud je explicitní hodnota action stejná jako okolní hodnota pro action, id lze použít hodnotu.
  • Pokud explicitní hodnoty obsahují hodnotu pro controller, je ignorována jakákoli okolní hodnota pro controller . Pokud se explicitní hodnota pro jinou než okolní hodnota controller controllerpro , action hodnoty a id hodnoty nebudou použity. Pokud je explicitní hodnota controller stejná jako okolní hodnota pro controller, action lze použít hodnoty a id hodnoty.

Tento proces je ještě složitější díky existenci tras atributů a vyhrazených konvenčních tras. Běžné trasy kontroleru, jako {controller}/{action}/{id?} je například určení hierarchie pomocí parametrů trasy. Pro vyhrazené konvenční trasy a trasy atributů pro kontrolery a Razor stránky:

  • Existuje hierarchie hodnot tras.
  • Nezobrazují se v šabloně.

V těchto případech generování adresy URL definuje koncept požadovaných hodnot . Koncové body vytvořené kontrolery a Razor stránkami mají zadané požadované hodnoty, které umožňují zneplatnění hodnoty trasy.

Podrobný algoritmus zneplatnění hodnoty trasy:

  • Požadované názvy hodnot se kombinují s parametry trasy a pak se zpracovávají zleva doprava.
  • Pro každý parametr se porovná okolní hodnota a explicitní hodnota:
    • Pokud je okolní hodnota a explicitní hodnota stejné, proces pokračuje.
    • Pokud je okolní hodnota přítomná a explicitní hodnota není, použije se při generování adresy URL okolní hodnota.
    • Pokud okolní hodnota není přítomná a explicitní hodnota je, zamítněte okolní hodnotu a všechny následné okolní hodnoty.
    • Pokud se nachází okolí a explicitní hodnota a obě hodnoty jsou odlišné, zamítněte okolní hodnotu a všechny následné okolní hodnoty.

V tuto chvíli je operace generování adres URL připravená k vyhodnocení omezení trasy. Sada přijatých hodnot se zkombinuje s výchozími hodnotami parametrů, které jsou k dispozici pro omezení. Pokud všechna omezení projdou, operace bude pokračovat.

Dále je možné použít akceptované hodnoty k rozšíření šablony trasy. Šablona trasy se zpracuje:

  • Zleva doprava.
  • Každý parametr má jeho přijatou hodnotu nahrazenou.
  • S následujícími zvláštními případy:
    • Pokud přijaté hodnoty chybí hodnota a parametr má výchozí hodnotu, použije se výchozí hodnota.
    • Pokud přijaté hodnoty chybí a parametr je nepovinný, zpracování pokračuje.
    • Pokud některý parametr trasy napravo od chybějícího volitelného parametru má hodnotu, operace selže.
    • Pokud je to možné, sbalí se souvislé parametry s výchozími hodnotami a volitelné parametry.

Hodnoty explicitně za předpokladu, že neodpovídají segmentu trasy, se přidají do řetězce dotazu. Následující tabulka ukazuje výsledek při použití šablony {controller}/{action}/{id?}trasy .

Okolní hodnoty Explicitní hodnoty Výsledek
controller = "Home" action = "O aplikaci" /Home/About
controller = "Home" controller = "Order", action = "About" /Order/About
controller = "Home", color = "Red" action = "O aplikaci" /Home/About
controller = "Home" action = "O produktu", barva = "Červená" /Home/About?color=Red

Volitelné pořadí parametrů trasy

Volitelné parametry trasy musí pocházet po všech požadovaných parametrech trasy a literálech. V následujícím kódu id name musí parametry pocházet za parametrem color :

using Microsoft.AspNetCore.Mvc;

namespace WebApplication1.Controllers;

[Route("api/[controller]")]
public class MyController : ControllerBase
{
    // GET /api/my/red/2/joe
    // GET /api/my/red/2
    // GET /api/my
    [HttpGet("{color}/{id:int?}/{name?}")]
    public IActionResult GetByIdAndOptionalName(string color, int id = 1, string? name = null)
    {
        return Ok($"{color} {id} {name ?? ""}");
    }
}

Problémy s neplatnou hodnotou trasy

Následující kód ukazuje příklad schématu generování adres URL, které není podporováno směrováním:

app.MapControllerRoute(
    "default",
    "{culture}/{controller=Home}/{action=Index}/{id?}");

app.MapControllerRoute(
    "blog",
    "{culture}/{**slug}",
    new { controller = "Blog", action = "ReadPost" });

V předchozím kódu culture se parametr trasy používá pro lokalizaci. Touha má parametr culture vždy přijmout jako okolní hodnotu. Parametr culture však není přijat jako okolní hodnota kvůli způsobu, jakým požadované hodnoty fungují:

  • "default" V šabloně culture trasy je parametr trasy vlevo od controller, takže změny controller nebudou cultureneplatné .
  • "blog" V šabloně culture trasy se parametr trasy považuje za napravo od controllerparametru trasy, který se zobrazí v požadovaných hodnotách.

Parsování cest URL pomocí LinkParser

Třída LinkParser přidává podporu analýzy cesty URL do sady hodnot tras. Metoda ParsePathByEndpointName přebírá název koncového bodu a cestu URL a vrátí sadu hodnot tras extrahovaných z cesty URL.

V následujícím příkladu kontroler akce GetProduct používá šablonu api/Products/{id} trasy a má Name :GetProduct

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}", Name = nameof(GetProduct))]
    public IActionResult GetProduct(string id)
    {
        // ...

Ve stejné třídě AddRelatedProduct kontroleru akce očekává cestu URL, pathToRelatedProductkterou lze poskytnout jako parametr řetězce dotazu:

[HttpPost("{id}/Related")]
public IActionResult AddRelatedProduct(
    string id, string pathToRelatedProduct, [FromServices] LinkParser linkParser)
{
    var routeValues = linkParser.ParsePathByEndpointName(
        nameof(GetProduct), pathToRelatedProduct);
    var relatedProductId = routeValues?["id"];

    // ...

V předchozím příkladu AddRelatedProduct akce extrahuje id hodnotu trasy z cesty URL. Například s cestou /api/Products/1URL , relatedProductId hodnota je nastavena na 1. Tento přístup umožňuje klientům rozhraní API používat cesty url při odkazování na prostředky, aniž by museli vědět, jak je taková adresa URL strukturovaná.

Konfigurace metadat koncového bodu

Následující odkazy obsahují informace o konfiguraci metadat koncového bodu:

Porovnávání hostitelů v trasách pomocí RequireHost

RequireHost použije omezení na trasu, která vyžaduje zadaného hostitele. Parametrem RequireHost [Host] může být:

  • Hostitel: www.domain.com, odpovídá www.domain.com libovolnému portu.
  • Hostitel se zástupným znakem: *.domain.com, shody www.domain.com, subdomain.domain.comnebo www.subdomain.domain.com na libovolném portu.
  • Port: *:5000Odpovídá portu 5000 s libovolným hostitelem.
  • Hostitel a port: www.domain.com:5000 nebo *.domain.com:5000odpovídá hostiteli a portu.

Lze zadat více parametrů pomocí RequireHost nebo [Host]. Omezení odpovídá hostitelům platným pro některý z parametrů. Například [Host("domain.com", "*.domain.com")] shody domain.com, www.domain.coma subdomain.domain.com.

Následující kód používá RequireHost k vyžadování zadaného hostitele na trase:

app.MapGet("/", () => "Contoso").RequireHost("contoso.com");
app.MapGet("/", () => "AdventureWorks").RequireHost("adventure-works.com");

app.MapHealthChecks("/healthz").RequireHost("*:8080");

Následující kód používá [Host] atribut na kontroleru k vyžadování některého ze zadaných hostitelů:

[Host("contoso.com", "adventure-works.com")]
public class HostsController : Controller
{
    public IActionResult Index() =>
        View();

    [Host("example.com")]
    public IActionResult Example() =>
        View();
}

[Host] Když se atribut použije na metodu kontroleru i akce:

  • Použije se atribut akce.
  • Atribut kontroleru je ignorován.

Upozorňující

Rozhraní API, které závisí na hlavičce hostitele, například HttpRequest.Host a RequireHost, podléhají potenciálnímu falšování identity klienty.

Pokud chcete zabránit falšování identity hostitele a portu, použijte jeden z následujících přístupů:

Skupiny tras

Metoda MapGroup rozšíření pomáhá uspořádat skupiny koncových bodů s běžnou předponou. Snižuje opakující se kód a umožňuje přizpůsobit celé skupiny koncových bodů jediným voláním metod, jako RequireAuthorization jsou a WithMetadata které přidávají metadata koncového bodu.

Například následující kód vytvoří dvě podobné skupiny koncových bodů:

app.MapGroup("/public/todos")
    .MapTodosApi()
    .WithTags("Public");

app.MapGroup("/private/todos")
    .MapTodosApi()
    .WithTags("Private")
    .AddEndpointFilterFactory(QueryPrivateTodos)
    .RequireAuthorization();


EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
    var dbContextIndex = -1;

    foreach (var argument in factoryContext.MethodInfo.GetParameters())
    {
        if (argument.ParameterType == typeof(TodoDb))
        {
            dbContextIndex = argument.Position;
            break;
        }
    }

    // Skip filter if the method doesn't have a TodoDb parameter.
    if (dbContextIndex < 0)
    {
        return next;
    }

    return async invocationContext =>
    {
        var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
        dbContext.IsPrivate = true;

        try
        {
            return await next(invocationContext);
        }
        finally
        {
            // This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
            dbContext.IsPrivate = false;
        }
    };
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
    group.MapGet("/", GetAllTodos);
    group.MapGet("/{id}", GetTodo);
    group.MapPost("/", CreateTodo);
    group.MapPut("/{id}", UpdateTodo);
    group.MapDelete("/{id}", DeleteTodo);

    return group;
}

V tomto scénáři můžete použít relativní adresu hlavičky Location ve výsledku 201 Created :

public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
    await database.AddAsync(todo);
    await database.SaveChangesAsync();

    return TypedResults.Created($"{todo.Id}", todo);
}

První skupina koncových bodů se bude shodovat pouze s požadavky s předponou /public/todos a jsou přístupná bez ověřování. Druhá skupina koncových bodů bude odpovídat pouze požadavkům s předponou /private/todos a vyžaduje ověření.

Objekt QueryPrivateTodos pro filtrování koncových bodů je místní funkce, která upravuje parametry obslužné rutiny TodoDb trasy tak, aby umožňovala přístup k privátním datům úkolů a jejich ukládání.

Skupiny tras také podporují vnořené skupiny a složité vzory předpon s parametry trasy a omezeními. V následujícím příkladu a obslužná rutina trasy namapovaná na user skupinu může zachytit {org} parametry a {group} parametry trasy definované v předponách vnější skupiny.

Předpona může být také prázdná. To může být užitečné pro přidání metadat koncového bodu nebo filtrů do skupiny koncových bodů beze změny vzoru trasy.

var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");

Přidání filtrů nebo metadat do skupiny se chová stejně jako jejich individuální přidání do každého koncového bodu před přidáním dalších filtrů nebo metadat, které mohly být přidány do vnitřní skupiny nebo konkrétního koncového bodu.

var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");

inner.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/inner group filter");
    return next(context);
});

outer.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/outer group filter");
    return next(context);
});

inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("MapGet filter");
    return next(context);
});

V předchozím příkladu vnější filtr zapíše příchozí požadavek před vnitřním filtrem, i když byl přidán druhý. Vzhledem k tomu, že filtry byly použity u různých skupin, pořadí, které byly přidány vzhledem k sobě, nezáleží. Přidají se filtry objednávek bez ohledu na to, jestli se použije na stejnou skupinu nebo konkrétní koncový bod.

Žádost o /outer/inner/ protokolování bude následující:

/outer group filter
/inner group filter
MapGet filter

Doprovodné materiály k výkonu pro směrování

Pokud má aplikace problémy s výkonem, směrování se často považuje za problém. Důvodem je podezření, že architektury, jako jsou kontrolery a Razor stránky, hlásí dobu strávenou uvnitř rozhraní v protokolovacích zprávách. Pokud je mezi časem hlášeným kontrolery a celkovým časem požadavku významný rozdíl:

  • Vývojáři eliminují kód aplikace jako zdroj problému.
  • Běžně se předpokládá, že příčinou je směrování.

Směrování je testované výkonem pomocí tisíců koncových bodů. Je nepravděpodobné, že by typická aplikace narazila na problém s výkonem jenom tím, že je příliš velká. Nejčastější hlavní příčinou pomalého směrování je obvykle špatně se chovající vlastní middleware.

Následující ukázka kódu ukazuje základní techniku zúžení zdroje zpoždění:

var logger = app.Services.GetRequiredService<ILogger<Program>>();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.UseRouting();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.UseAuthorization();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 3: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.MapGet("/", () => "Timing Test.");

Směrování podle času:

  • Prokládání každého middlewaru pomocí kopie middlewaru časování zobrazeného v předchozím kódu.
  • Přidejte jedinečný identifikátor pro korelaci dat časování s kódem.

Jedná se o základní způsob, jak zúžit zpoždění, pokud je významné, například více než 10ms. Odečítá Time 2 se od Time 1 sestav čas strávený v middlewaru UseRouting .

Následující kód používá kompaktnější přístup k předchozímu kódu časování:

public sealed class AutoStopwatch : IDisposable
{
    private readonly ILogger _logger;
    private readonly string _message;
    private readonly Stopwatch _stopwatch;
    private bool _disposed;

    public AutoStopwatch(ILogger logger, string message) =>
        (_logger, _message, _stopwatch) = (logger, message, Stopwatch.StartNew());

    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        _logger.LogInformation("{Message}: {ElapsedMilliseconds}ms",
            _message, _stopwatch.ElapsedMilliseconds);

        _disposed = true;
    }
}
var logger = app.Services.GetRequiredService<ILogger<Program>>();
var timerCount = 0;

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.UseRouting();

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.UseAuthorization();

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.MapGet("/", () => "Timing Test.");

Potenciálně nákladné funkce směrování

Následující seznam obsahuje přehled o funkcích směrování, které jsou relativně drahé v porovnání se základními šablonami tras:

  • Regulární výrazy: Je možné psát regulární výrazy, které jsou složité nebo mají dlouhou dobu trvání s malým množstvím vstupu.
  • Komplexní segmenty ({x}-{y}-{z}):
    • Jsou výrazně dražší než analýza běžného segmentu cesty URL.
    • Výsledkem je přidělení mnoha dalších podřetěžců.
  • Synchronní přístup k datům: Mnoho složitých aplikací má v rámci směrování přístup k databázi. Použijte body rozšiřitelnosti, například MatcherPolicy a EndpointSelectorContext, které jsou asynchronní.

Pokyny pro velké směrovací tabulky

Ve výchozím nastavení ASP.NET Core používá algoritmus směrování, který obchoduje s pamětí za čas procesoru. To má pěkný účinek, že čas porovnávání tras závisí pouze na délce cesty, která se má shodovat, a ne na počtu tras. Tento přístup ale může být v některých případech problematický, když má aplikace velký počet tras (v tisících) a v trasách existuje velké množství předpon proměnných. Například pokud trasy mají parametry v počátečních segmentech trasy, například {parameter}/some/literal.

Je nepravděpodobné, že by aplikace narazila na situaci, kdy se jedná o problém, pokud:

  • V aplikaci existuje velký počet tras, které tento model používají.
  • V aplikaci je velký počet tras.

Jak zjistit, jestli aplikace narazí na problém s velkou směrovací tabulkou

  • Existují dva příznaky, které je třeba hledat:
    • Aplikace se pomalu spustí při prvním požadavku.
      • Mějte na paměti, že je to povinné, ale nestačí. Existuje mnoho dalších problémů, které se nesměrují, než může způsobit pomalé spuštění aplikace. Zkontrolujte níže uvedený stav, abyste přesně zjistili, jestli aplikace v této situaci neběží.
    • Aplikace během spouštění spotřebovává velké množství paměti a výpis paměti zobrazuje velký počet Microsoft.AspNetCore.Routing.Matching.DfaNode instancí.

Jak tento problém vyřešit

Existuje několik technik a optimalizací, které je možné použít u tras, které tento scénář z velké části zlepšují:

  • Pokud je to možné, použijte omezení trasy na parametry, například {parameter:int}{parameter:guid}, {parameter:regex(\\d+)}, atd.
    • To umožňuje algoritmus směrování interně optimalizovat struktury používané pro porovnávání a výrazně snížit využitou paměť.
    • Ve většině případů to stačí, abyste se vrátili k přijatelnému chování.
  • Změňte trasy tak, aby se parametry přesunuly do pozdějších segmentů v šabloně.
    • Tím se sníží počet možných cest tak, aby odpovídal koncovému bodu dané cestě.
  • Použijte dynamickou trasu a dynamicky proveďte mapování na kontroler nebo stránku.
    • Toho lze dosáhnout pomocí MapDynamicControllerRoute a MapDynamicPageRoute.

Middleware s krátkým okruhem po směrování

Když směrování odpovídá koncovému bodu, obvykle umožňuje rest spuštění kanálu middlewaru před vyvoláním logiky koncového bodu. Služby můžou snížit využití prostředků filtrováním známých požadavků v rané fázi kanálu. Metodu ShortCircuit rozšíření použijte k okamžitému vyvolání logiky koncového bodu směrování a následnému ukončení požadavku. Například daná trasa nemusí procházet ověřováním nebo middlewarem CORS. Následující příklad žádostí o zkraty, které odpovídají /short-circuit trase:

app.MapGet("/short-circuit", () => "Short circuiting!").ShortCircuit();

Metoda ShortCircuit(IEndpointConventionBuilder, Nullable<Int32>) může volitelně převzít stavový kód.

MapShortCircuit Pomocí metody můžete nastavit zkratování pro více tras najednou předáním pole parametrů s předponami adresy URL. Například prohlížeče a roboti často testují servery pro dobře známé cesty jako robots.txt a favicon.ico. Pokud aplikace tyto soubory nemá, může jeden řádek kódu nakonfigurovat obě trasy:

app.MapShortCircuit(404, "robots.txt", "favicon.ico");

MapShortCircuit vrátí, IEndpointConventionBuilder aby do ní bylo možné přidat další omezení směrování, jako je filtrování hostitelů.

MapShortCircuit Metody nemají vliv na ShortCircuit middleware umístěný před UseRouting. Při pokusu o použití těchto metod s koncovými body, které mají [Authorize] také metadata, [RequireCors] způsobí selhání požadavků s chybou InvalidOperationException. Tato metadata se použijí pomocí [Authorize] [EnableCors] atributů nebo RequireCors metod nebo RequireAuthorization metod.

Chcete-li zobrazit účinek zkratového middlewaru, nastavte kategorii protokolování "Microsoft" na "Informace" v appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Information",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

Spusťte následující kód:

var app = WebApplication.Create();

app.UseHttpLogging();

app.MapGet("/", () => "No short-circuiting!");
app.MapGet("/short-circuit", () => "Short circuiting!").ShortCircuit();
app.MapShortCircuit(404, "robots.txt", "favicon.ico");

app.Run();

Následující příklad pochází z protokolů konzoly vytvořených spuštěním koncového / bodu. Zahrnuje výstup z middlewaru protokolování:

info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint 'HTTP: GET /'
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'HTTP: GET /'
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      StatusCode: 200
      Content-Type: text/plain; charset=utf-8
      Date: Wed, 03 May 2023 21:05:59 GMT
      Server: Kestrel
      Alt-Svc: h3=":5182"; ma=86400
      Transfer-Encoding: chunked

Následující příklad pochází ze spuštění koncového /short-circuit bodu. Nemá nic z middlewaru protokolování, protože middleware byl zkrácený:

info: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[4]
      The endpoint 'HTTP: GET /short-circuit' is being executed without running additional middleware.
info: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[5]
      The endpoint 'HTTP: GET /short-circuit' has been executed without running additional middleware.

Pokyny pro autory knihoven

Tato část obsahuje pokyny pro autory knihoven, kteří vycházejí ze směrování. Cílem těchto podrobností je zajistit, aby vývojáři aplikací měli dobré zkušenosti s používáním knihoven a architektur, které rozšiřují směrování.

Definování koncových bodů

Pokud chcete vytvořit architekturu, která používá směrování pro porovnávání adres URL, začněte definováním uživatelského prostředí, které je postavené na UseEndpoints.

DO staví na vrcholu IEndpointRouteBuilder. To umožňuje uživatelům vytvářet architekturu s jinými funkcemi ASP.NET Core bez nejasností. Každá šablona ASP.NET Core zahrnuje směrování. Předpokládejme, že směrování je pro uživatele k dispozici a známé.

// Your framework
app.MapMyFramework(...);

app.MapHealthChecks("/healthz");

DO vrátit zapečetěný beton typ z volání MapMyFramework(...) , které implementuje IEndpointConventionBuilder. Většina metod architektury Map... tento vzor dodržuje. Rozhraní IEndpointConventionBuilder :

  • Umožňuje složená metadata.
  • Cílí na různé rozšiřující metody.

Deklarování vlastního typu umožňuje do tvůrce přidat vlastní funkce specifické pro architekturu. Je v pořádku zabalit tvůrce deklarovaného architekturou a předat do něj volání.

// Your framework
app.MapMyFramework(...)
    .RequireAuthorization()
    .WithMyFrameworkFeature(awesome: true);

app.MapHealthChecks("/healthz");

ZVAŽTE psaní vlastního EndpointDataSource. EndpointDataSource je primitivní úroveň nízké úrovně pro deklarování a aktualizaci kolekce koncových bodů. EndpointDataSource je výkonné rozhraní API používané kontrolery a Razor stránkami. Další informace najdete v tématu Dynamické směrování koncových bodů.

Testy směrování mají základní příklad neaktualizuje zdroje dat.

ZVAŽTE implementaci GetGroupedEndpoints. Tím získáte úplnou kontrolu nad spouštěním konvencí skupin a konečnými metadaty ve seskupených koncových bodech. To například umožňuje vlastní EndpointDataSource implementaci spouštět filtry koncových bodů přidané do skupin.

VE výchozím nastavení se nepokoušejte EndpointDataSource o registraci. Vyžadovat, aby uživatelé zaregistrovali vaši architekturu v UseEndpoints. Filozofie směrování spočívá v tom, že ve výchozím nastavení se nic nezahrnuje a je to UseEndpoints místo pro registraci koncových bodů.

Vytvoření middlewaru integrovaného se směrováním

ZVAŽTE definování typů metadat jako rozhraní.

Umožňuje použít typy metadat jako atribut tříd a metod.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => true;
}

Architektury, jako jsou kontrolery a Razor stránky, podporují použití atributů metadat na typy a metody. Pokud deklarujete typy metadat:

  • Zpřístupní je jako atributy.
  • Většina uživatelů má zkušenosti s používáním atributů.

Deklarace typu metadat jako rozhraní přidává další vrstvu flexibility:

  • Rozhraní jsou kompozibilní.
  • Vývojáři můžou deklarovat své vlastní typy, které kombinují více zásad.

Umožňuje přepsat metadata, jak je znázorněno v následujícím příkladu:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SuppressCoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => false;
}

[CoolMetadata]
public class MyController : Controller
{
    public void MyCool() { }

    [SuppressCoolMetadata]
    public void Uncool() { }
}

Nejlepší způsob, jak postupovat podle těchto pokynů, je vyhnout se definování metadat značek:

  • Nehledáte jenom přítomnost typu metadat.
  • Definujte vlastnost metadat a zkontrolujte vlastnost.

Kolekce metadat je seřazená a podporuje přepsání podle priority. V případě kontrolerů jsou metadata pro metodu akce nejvýraznější.

Do make middleware užitečný s a bez směrování:

app.UseAuthorization(new AuthorizationPolicy() { ... });

// Your framework
app.MapMyFramework(...).RequireAuthorization();

Jako příklad tohoto návodu UseAuthorization zvažte middleware. Middleware pro autorizaci umožňuje předávat záložní zásady. Záložní zásada, pokud je zadaná, platí pro obě:

  • Koncové body bez zadané zásady
  • Požadavky, které neodpovídají koncovému bodu.

Díky tomu je middleware autorizace užitečný mimo kontext směrování. Autorizační middleware lze použít pro tradiční programování middlewaru.

Ladění diagnostiky

Podrobný výstup diagnostiky směrování nastavíte Logging:LogLevel:Microsoft na Debughodnotu . Ve vývojovém prostředí nastavte úroveň protokolu v appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

Další materiály

Směrování zodpovídá za porovnávání příchozích požadavků HTTP a odesílání těchto požadavků do spustitelných koncových bodů aplikace. Koncové body jsou jednotky spustitelného kódu zpracování požadavků. Koncové body se definují v aplikaci a konfigurují se při spuštění aplikace. Proces porovnávání koncových bodů může extrahovat hodnoty z adresy URL požadavku a poskytnout tyto hodnoty pro zpracování požadavků. Pomocí informací o koncovém bodu z aplikace je směrování také schopné generovat adresy URL, které se mapují na koncové body.

Aplikace můžou nakonfigurovat směrování pomocí:

Tento článek se zabývá podrobnostmi nízké úrovně směrování ASP.NET Core. Informace o konfiguraci směrování:

Základy směrování

Následující kód ukazuje základní příklad směrování:

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

app.MapGet("/", () => "Hello World!");

app.Run();

Předchozí příklad obsahuje jeden koncový bod pomocí MapGet metody:

  • Při odeslání požadavku HTTP GET na kořenovou adresu URL /:
    • Delegát požadavku se spustí.
    • Hello World! je zapsán do odpovědi HTTP.
  • Pokud metoda požadavku není GET nebo kořenová adresa URL není /, žádná trasa neodpovídá a vrátí se http 404.

Směrování používá dvojici middlewaru, zaregistrovaného uživatelem UseRouting a UseEndpoints:

  • UseRouting přidá trasu odpovídající kanálu middlewaru. Tento middleware se podívá na sadu koncových bodů definovaných v aplikaci a vybere nejlepší shodu na základě požadavku.
  • UseEndpoints přidá spuštění koncového bodu do kanálu middlewaru. Spustí delegáta přidruženého k vybranému koncovému bodu.

Aplikace obvykle nemusí volat UseRouting ani UseEndpoints. WebApplicationBuilder konfiguruje middlewarový kanál, který zabalí middleware přidaný do Program.cs UseRouting a UseEndpoints. Aplikace ale můžou změnit pořadí, ve kterém UseRouting se budou spouštět, UseEndpoints a to explicitním voláním těchto metod. Například následující kód provede explicitní volání UseRouting:

app.Use(async (context, next) =>
{
    // ...
    await next(context);
});

app.UseRouting();

app.MapGet("/", () => "Hello World!");

V předchozím kódu:

  • Volání pro app.Use registraci vlastního middlewaru, který se spouští na začátku kanálu.
  • Volání konfiguruje UseRouting middleware odpovídající směrování tak, aby se spustil po vlastním middlewaru.
  • Koncový bod zaregistrovaný na MapGet konci kanálu se spustí.

Pokud by předchozí příklad nezahrnoval volání UseRouting, vlastní middleware by se spustil za middlewarem odpovídajícím trasě.

Koncové body

Metoda MapGet se používá k definování koncového bodu. Koncový bod je něco, co může být:

  • Vybráno tak, že se shoduje s adresou URL a metodou HTTP.
  • Spustí se spuštěním delegáta.

Koncové body, které se dají spárovat a spouštět aplikací, jsou nakonfigurované v UseEndpoints. Například MapGet, MapPosta podobné metody připojit žádosti delegáty do systému směrování. Další metody lze použít k připojení funkcí architektury ASP.NET Core ke směrovacímu systému:

Následující příklad ukazuje směrování s sofistikovanější šablonou trasy:

app.MapGet("/hello/{name:alpha}", (string name) => $"Hello {name}!");

Řetězec /hello/{name:alpha} je šablona trasy. Ke konfiguraci shody koncového bodu se používá šablona trasy. V tomto případě šablona odpovídá:

  • Adresa URL, jako je /hello/Docs
  • Všechny cesty URL, které začínají /hello/ posloupností abecedních znaků. :alpha použije omezení trasy, které odpovídá pouze abecedním znakům. Omezení tras jsou vysvětlena dále v tomto článku.

Druhý segment cesty URL: {name:alpha}

Následující příklad ukazuje směrování s kontrolami stavu a autorizací:

app.UseAuthentication();
app.UseAuthorization();

app.MapHealthChecks("/healthz").RequireAuthorization();
app.MapGet("/", () => "Hello World!");

Předchozí příklad ukazuje, jak:

  • S směrováním je možné použít autorizační middleware.
  • Koncové body je možné použít ke konfiguraci chování autorizace.

Volání MapHealthChecks přidá koncový bod kontroly stavu. Při zřetězování RequireAuthorization k tomuto volání se ke koncovému bodu připojí zásady autorizace.

Volání UseAuthentication a UseAuthorization přidání middlewaru pro ověřování a autorizaci Tyto middleware jsou umístěny mezi UseRouting a UseEndpoints tak, aby mohly:

  • Podívejte se, který koncový bod vybral UseRouting.
  • Před odesláním do koncového bodu použijte zásadu UseEndpoints autorizace.

Metadata koncového bodu

V předchozím příkladu existují dva koncové body, ale k autorizační zásadě jsou připojené jenom koncové body kontroly stavu. Pokud požadavek odpovídá koncovému bodu kontroly stavu, /healthzprovede se kontrola autorizace. To ukazuje, že koncové body můžou mít připojená další data. Tato další data se nazývají metadata koncového bodu:

  • Metadata je možné zpracovat pomocí middlewaru pracujícího se směrováním.
  • Metadata můžou být libovolného typu .NET.

Koncepty směrování

Systém směrování vychází z kanálu middlewaru přidáním výkonného konceptu koncového bodu . Koncové body představují jednotky funkcí aplikace, které se vzájemně liší z hlediska směrování, autorizace a libovolného počtu systémů ASP.NET Core.

Definice koncového bodu ASP.NET Core

Koncový bod ASP.NET Core je:

Následující kód ukazuje, jak načíst a zkontrolovat koncový bod odpovídající aktuálnímu požadavku:

app.Use(async (context, next) =>
{
    var currentEndpoint = context.GetEndpoint();

    if (currentEndpoint is null)
    {
        await next(context);
        return;
    }

    Console.WriteLine($"Endpoint: {currentEndpoint.DisplayName}");

    if (currentEndpoint is RouteEndpoint routeEndpoint)
    {
        Console.WriteLine($"  - Route Pattern: {routeEndpoint.RoutePattern}");
    }

    foreach (var endpointMetadata in currentEndpoint.Metadata)
    {
        Console.WriteLine($"  - Metadata: {endpointMetadata}");
    }

    await next(context);
});

app.MapGet("/", () => "Inspect Endpoint.");

Koncový bod, pokud je vybraný, lze načíst z objektu HttpContext. Jeho vlastnosti je možné zkontrolovat. Objekty koncového bodu jsou neměnné a po vytvoření není možné je změnit. Nejběžnějším typem RouteEndpointkoncového bodu je . RouteEndpoint obsahuje informace, které mu umožní vybrat směrovací systém.

V předchozím kódu aplikace. Slouží ke konfiguraci vloženého middlewaru.

Následující kód ukazuje, že v závislosti na tom, kde app.Use se v kanálu volá, nemusí existovat koncový bod:

// Location 1: before routing runs, endpoint is always null here.
app.Use(async (context, next) =>
{
    Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

app.UseRouting();

// Location 2: after routing runs, endpoint will be non-null if routing found a match.
app.Use(async (context, next) =>
{
    Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

// Location 3: runs when this endpoint matches
app.MapGet("/", (HttpContext context) =>
{
    Console.WriteLine($"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    return "Hello World!";
}).WithDisplayName("Hello");

app.UseEndpoints(_ => { });

// Location 4: runs after UseEndpoints - will only run if there was no match.
app.Use(async (context, next) =>
{
    Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

Předchozí ukázka přidá Console.WriteLine příkazy, které zobrazují, jestli byl vybrán koncový bod nebo ne. Pro přehlednost ukázka přiřadí zadaný koncový bod zobrazovaný název / .

Předchozí ukázka také zahrnuje volání UseRouting a UseEndpoints řízení přesně toho, kdy se tyto middlewary spouští v rámci kanálu.

Spuštění tohoto kódu s adresou URL / zobrazení:

1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello

Spuštění tohoto kódu s jakoukoli jinou adresou URL zobrazí:

1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)

Tento výstup ukazuje, že:

  • Koncový bod je před zavolání vždy null UseRouting .
  • Pokud se najde shoda, koncový bod je mezi UseRouting a UseEndpoints.
  • Middleware UseEndpoints je terminál , když se najde shoda. Middleware terminálu je definován dále v tomto článku.
  • Middleware po spuštění pouze v UseEndpoints případě, že nebyla nalezena žádná shoda.

Middleware UseRouting používá metodu SetEndpoint pro připojení koncového bodu k aktuálnímu kontextu. Middleware je možné nahradit UseRouting vlastní logikou a přesto získat výhody používání koncových bodů. Koncové body jsou primitivní nízké úrovně, jako je middleware, a nejsou svázané s implementací směrování. Většina aplikací nemusí nahradit UseRouting vlastní logikou.

Middleware UseEndpoints je navržený tak, aby se používal společně s middlewarem UseRouting . Základní logika pro spuštění koncového bodu není složitá. Slouží GetEndpoint k načtení koncového bodu a následnému vyvolání jeho RequestDelegate vlastnosti.

Následující kód ukazuje, jak může middleware ovlivnit nebo reagovat na směrování:

app.UseHttpMethodOverride();
app.UseRouting();

app.Use(async (context, next) =>
{
    if (context.GetEndpoint()?.Metadata.GetMetadata<RequiresAuditAttribute>() is not null)
    {
        Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
    }

    await next(context);
});

app.MapGet("/", () => "Audit isn't required.");
app.MapGet("/sensitive", () => "Audit required for sensitive data.")
    .WithMetadata(new RequiresAuditAttribute());
public class RequiresAuditAttribute : Attribute { }

Předchozí příklad ukazuje dva důležité koncepty:

  • Middleware se může spustit před UseRouting úpravou dat, se kterými směrování pracuje.
  • Middleware se může spustit mezi UseRouting směrováním a UseEndpoints zpracovat výsledky směrování před spuštěním koncového bodu.
    • Middleware, který běží mezi UseRouting a UseEndpoints:
      • Obvykle kontroluje metadata, aby porozuměla koncovým bodům.
      • Často provádí rozhodnutí o zabezpečení, jak to dělá UseAuthorization a UseCors.
    • Kombinace middlewaru a metadat umožňuje konfigurovat zásady pro jednotlivé koncové body.

Předchozí kód ukazuje příklad vlastního middlewaru, který podporuje zásady pro jednotlivé koncové body. Middleware zapíše protokol auditu přístupu k citlivým datům do konzoly. Middleware je možné nakonfigurovat tak, aby auditoval koncový bod s RequiresAuditAttribute metadaty. Tato ukázka ukazuje způsob vyjádření výslovného souhlasu, kdy se auditují jenom koncové body označené jako citlivé. Tuto logiku je možné definovat obráceně, auditovat všechno, co není označené jako bezpečné, například. Systém metadat koncového bodu je flexibilní. Tato logika by mohla být navržena jakýmkoli způsobem, který vyhovuje případu použití.

Předchozí vzorový kód je určený k předvedení základních konceptů koncových bodů. Ukázka není určená pro produkční použití. Ucelenější verze middlewaru protokolu auditu:

  • Přihlaste se k souboru nebo databázi.
  • Uveďte podrobnosti, jako je uživatel, IP adresa, název citlivého koncového bodu a další.

Metadata zásad auditu jsou definována RequiresAuditAttribute jako Attribute jednodušší použití s architekturami založenými na třídách, jako jsou kontrolery a SignalR. Při použití trasy ke kódu:

  • Metadata jsou připojena pomocí rozhraní API tvůrce.
  • Architektury založené na třídách zahrnují všechny atributy odpovídající metody a třídy při vytváření koncových bodů.

Osvědčenými postupy pro typy metadat je definovat buď jako rozhraní, nebo atributy. Rozhraní a atributy umožňují opakované použití kódu. Systém metadat je flexibilní a neukládá žádná omezení.

Porovnání middlewaru terminálu se směrováním

Následující příklad ukazuje terminálový middleware i směrování:

// Approach 1: Terminal Middleware.
app.Use(async (context, next) =>
{
    if (context.Request.Path == "/")
    {
        await context.Response.WriteAsync("Terminal Middleware.");
        return;
    }

    await next(context);
});

app.UseRouting();

// Approach 2: Routing.
app.MapGet("/Routing", () => "Routing.");

Styl middlewaru zobrazeného s terminálovým middlewarem Approach 1: . Říká se tomu middleware terminálu, protože dělá odpovídající operaci:

  • Odpovídající operace v předchozí ukázce je Path == "/" pro middleware a Path == "/Routing" pro směrování.
  • Když je shoda úspěšná, spustí některé funkce a vrátí se místo vyvolání middlewaru next .

Říká se tomu middleware terminálu, protože ukončí hledání, spustí některé funkce a vrátí se.

Následující seznam porovnává middleware terminálu se směrováním:

  • Oba přístupy umožňují ukončení kanálu zpracování:
    • Middleware ukončí kanál tím, že se místo vyvolání next.
    • Koncové body jsou vždy terminálové.
  • Middleware terminálu umožňuje umístění middlewaru na libovolné místo v kanálu:
  • Middleware terminálu umožňuje libovolnému kódu určit, kdy middleware odpovídá:
    • Vlastní kód pro porovnávání tras může být podrobný a obtížně zapisuje správně.
    • Směrování poskytuje jednoduchá řešení pro typické aplikace. Většina aplikací nevyžaduje vlastní kód pro porovnávání tras.
  • Rozhraní koncových bodů s middlewarem, jako UseAuthorization je například a UseCors.
    • Použití middlewaru terminálu s UseAuthorization autorizačním systémem nebo UseCors vyžaduje ruční propojení se systémem autorizace.

Koncový bod definuje obojí:

  • Delegát na zpracování požadavků.
  • Kolekce libovolných metadat. Metadata se používají k implementaci průřezových aspektů na základě zásad a konfigurace připojených k jednotlivým koncovým bodům.

Terminálový middleware může být efektivní nástroj, ale může vyžadovat:

  • Značné množství kódování a testování.
  • Ruční integrace s jinými systémy za účelem dosažení požadované úrovně flexibility.

Před zápisem middlewaru terminálu zvažte integraci se směrováním.

Existující middleware terminálu, který se integruje s mapou , nebo MapWhen se obvykle dá převést na koncový bod podporující směrování. MapHealthChecks ukazuje vzor pro router-ware:

Následující kód ukazuje použití MapHealthChecks:

app.UseAuthentication();
app.UseAuthorization();

app.MapHealthChecks("/healthz").RequireAuthorization();

Předchozí ukázka ukazuje, proč je vrácení objektu tvůrce důležité. Vrácení objektu tvůrce umožňuje vývojáři aplikací konfigurovat zásady, jako je autorizace pro koncový bod. V tomto příkladu nemá middleware kontroly stavu žádnou přímou integraci se systémem autorizace.

Systém metadat byl vytvořen v reakci na problémy zjištěné autory rozšiřitelnosti pomocí middlewaru terminálu. Pro každý middleware je problematické implementovat vlastní integraci s autorizačním systémem.

Porovnávání adres URL

  • Je proces, kterým směrování odpovídá příchozímu požadavku na koncový bod.
  • Je založená na datech v cestě URL a hlavičkách.
  • Můžete ho rozšířit, abyste zvážili všechna data v požadavku.

Když se spustí middleware směrování, nastaví Endpoint hodnotu požadavku na funkci HttpContext požadavku z aktuálního požadavku:

Middleware se spustí po spuštění middlewaru směrování, který může zkontrolovat koncový bod a provést akci. Například autorizační middleware může probrat kolekci metadat koncového bodu pro zásady autorizace. Po spuštění veškerého middlewaru v kanálu zpracování požadavků se vyvolá delegát vybraného koncového bodu.

Systém směrování ve směrování koncového bodu zodpovídá za veškerá rozhodnutí o odesílání. Vzhledem k tomu, že middleware používá zásady na základě vybraného koncového bodu, je důležité, aby:

  • Jakékoli rozhodnutí, které může ovlivnit odesílání nebo použití zásad zabezpečení, se provádí v rámci systému směrování.

Upozorňující

V případě zpětné kompatibility se při spuštění delegáta koncového RouteContext.RouteData bodu kontroleru nebo Razor stránky nastaví vlastnosti na odpovídající hodnoty na základě dosud provedeného zpracování požadavku.

Typ RouteContext bude v budoucí verzi označen jako zastaralý:

  • Migrovat RouteData.Values na HttpRequest.RouteValues.
  • Migrace RouteData.DataTokens pro načtení IDataTokensMetadata z metadat koncového bodu

Porovnávání adres URL funguje v konfigurovatelné sadě fází. V každé fázi je výstup sadou shod. Sadu shod lze dále zúžit v další fázi. Implementace směrování nezaručuje pořadí zpracování pro odpovídající koncové body. Všechny možné shody se zpracovávají najednou. Fáze porovnávání adres URL probíhají v následujícím pořadí. ASP.NET Core:

  1. Zpracovává cestu URL vůči sadě koncových bodů a jejich šablon tras a shromažďuje všechny shody.
  2. Vezme předchozí seznam a odebere shody, které selžou s použitými omezeními trasy.
  3. Vezme předchozí seznam a odebere shody, které sadu instancí selžou MatcherPolicy .
  4. EndpointSelector Použije k poslednímu rozhodnutí z předchozího seznamu.

Seznam koncových bodů má prioritu podle následujících:

Všechny odpovídající koncové body se zpracovávají v každé fázi, dokud se nedosáhne EndpointSelector . Jedná se EndpointSelector o konečnou fázi. Jako nejlepší shodu zvolí koncový bod s nejvyšší prioritou. Pokud existují jiné shody se stejnou prioritou jako nejlepší shoda, vyvolá se výjimka nejednoznačné shody.

Priorita trasy se vypočítá na základě konkrétnější šablony trasy, která má vyšší prioritu. Představte si například šablony /hello a /{message}:

  • Obě odpovídají cestě /helloURL .
  • /hello je konkrétnější a proto vyšší priorita.

Obecně platí, že priorita trasy má dobrou úlohu při výběru nejvhodnější shody pro druhy schémat adres URL používaných v praxi. Používejte Order pouze v případě potřeby, abyste se vyhnuli nejednoznačnosti.

Vzhledem k druhům rozšiřitelnosti poskytované směrováním není možné, aby směrovací systém předem počítaly nejednoznačné trasy. Představte si příklad, jako jsou šablony /{message:alpha} tras a /{message:int}:

  • Omezení alpha odpovídá pouze abecedním znakům.
  • Omezení int odpovídá pouze číslu.
  • Tyto šablony mají stejnou prioritu tras, ale obě adresy URL se neshodují.
  • Pokud systém směrování nahlásil při spuštění nejednoznačnosti, zablokoval by tento platný případ použití.

Upozorňující

Pořadí operací uvnitř UseEndpoints nemá vliv na chování směrování, s jednou výjimkou. MapControllerRoute a MapAreaRoute automaticky přiřadí hodnotu objednávky ke svým koncovým bodům na základě pořadí, ve které jsou vyvolány. To simuluje dlouhodobé chování kontrolerů bez systému směrování, který poskytuje stejné záruky jako starší implementace směrování.

Směrování koncových bodů v ASP.NET Core:

  • Nemá koncept tras.
  • Neposkytuje záruky objednávání. Všechny koncové body se zpracovávají najednou.

Priorita šablony trasy a pořadí výběru koncového bodu

Priorita šablony trasy je systém, který každé šabloně trasy přiřadí hodnotu na základě toho, jak je konkrétní. Priorita šablony trasy:

  • Vyhne se nutnosti upravovat pořadí koncových bodů v běžných případech.
  • Snaží se shodovat s očekáváními obecného chování směrování.

Představte si například šablony /Products/List a /Products/{id}. Bylo by rozumné předpokládat, že /Products/List je lepší shoda než /Products/{id} pro cestu /Products/ListURL . To funguje, protože literálový segment /List je považován za lepší prioritu než segment /{id}parametru .

Podrobnosti o tom, jak funguje priorita, jsou svázány s tím, jak se definují šablony tras:

  • Šablony s více segmenty jsou považovány za konkrétnější.
  • Segment s literálovým textem se považuje za konkrétnější než segment parametru.
  • Segment parametru s omezením se považuje za konkrétnější než jeden bez.
  • Složitý segment se považuje za specifický jako segment parametru s omezením.
  • Parametry pro zachytávání jsou nejmíň specifické. Důležité informace o trasách pro zachytávání všech tras najdete v části Šablony tras catch-all.

Koncepty generování adres URL

Generování adres URL:

  • Je proces, pomocí kterého směrování může vytvořit cestu URL na základě sady hodnot tras.
  • Umožňuje logické oddělení mezi koncovými body a adresami URL, které k nim přistupují.

Směrování koncových bodů zahrnuje LinkGenerator rozhraní API. LinkGenerator je jednoúčelová služba, která je k dispozici z DI. Rozhraní LinkGenerator API se dá použít mimo kontext spuštěného požadavku. Mvc.IUrlHelper a scénáře, které se spoléhají , IUrlHelperjako jsou pomocné rutiny značek, pomocné rutiny HTML a výsledky akcí, používají LinkGenerator rozhraní API interně k poskytování možností generování odkazů.

Generátor odkazů je podporován konceptem adresního a adresního schématu. Schéma adres je způsob, jak určit koncové body, které by se měly zvážit pro generování propojení. Například scénáře názvu trasy a směrovacích hodnot, které mnoho uživatelů znáte z kontrolerů a Razor Stránky se implementují jako schéma adres.

Generátor odkazů může odkazovat na kontrolery a Razor stránky pomocí následujících rozšiřujících metod:

Přetížení těchto metod přijímají argumenty, které zahrnují HttpContext. Tyto metody jsou funkčně ekvivalentní url.Action a Url.Page, ale nabízejí další flexibilitu a možnosti.

Metody GetPath* jsou nejvíce podobné Url.Action a Url.Pagev tom, že vygenerují identifikátor URI obsahující absolutní cestu. Metody GetUri* vždy generují absolutní identifikátor URI obsahující schéma a hostitele. Metody, které přijímají HttpContext vygenerování identifikátoru URI v kontextu prováděcího požadavku. Hodnoty okolní trasy, základní cesta URL, schéma a hostitel ze spuštěného požadavku se použijí, pokud je nepřepíšete.

LinkGenerator je volána s adresou. Generování identifikátoru URI probíhá ve dvou krocích:

  1. Adresa je svázaná se seznamem koncových bodů, které odpovídají adrese.
  2. Vyhodnocuje se RoutePattern každý koncový bod, dokud se nenajde vzor trasy, který odpovídá zadaným hodnotám. Výsledný výstup se zkombinuje s ostatními částmi identifikátoru URI zadanými do generátoru propojení a vrátí se.

Metody poskytované standardními možnostmi LinkGenerator generování propojení pro libovolný typ adresy. Nejpohodlnější způsob použití generátoru propojení je prostřednictvím rozšiřujících metod, které provádějí operace pro konkrétní typ adresy:

Extension – metoda Popis
GetPathByAddress Vygeneruje identifikátor URI s absolutní cestou na základě zadaných hodnot.
GetUriByAddress Vygeneruje absolutní identifikátor URI na základě zadaných hodnot.

Upozorňující

Věnujte pozornost následujícím důsledkům volání LinkGenerator metod:

  • V konfiguraci aplikace používejte GetUri* metody rozšíření s opatrností, která neověřuje hlavičku Host příchozích požadavků. Pokud hlavička Host příchozích požadavků není ověřená, může být nedůvěryhodný vstup požadavku odeslán zpět klientovi v identifikátorech URI v zobrazení nebo stránce. Doporučujeme, aby všechny produkční aplikace nakonfigurovaly svůj server tak, aby ověřovaly hlavičku Host proti známým platným hodnotám.

  • Používejte LinkGenerator s opatrností v middlewaru v kombinaci s Map nebo MapWhen. Map* změní základní cestu prováděcího požadavku, která má vliv na výstup generování propojení. LinkGenerator Všechna rozhraní API umožňují zadat základní cestu. Zadejte prázdnou Map* základní cestu, která vrátí zpět vliv na generování propojení.

Příklad middlewaru

V následujícím příkladu middleware používá LinkGenerator rozhraní API k vytvoření odkazu na metodu akce, která obsahuje seznam produktů pro ukládání. Použití generátoru odkazů vložením do třídy a volání GenerateLink je k dispozici pro libovolnou třídu v aplikaci:

public class ProductsMiddleware
{
    private readonly LinkGenerator _linkGenerator;

    public ProductsMiddleware(RequestDelegate next, LinkGenerator linkGenerator) =>
        _linkGenerator = linkGenerator;

    public async Task InvokeAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Plain;

        var productsPath = _linkGenerator.GetPathByAction("Products", "Store");

        await httpContext.Response.WriteAsync(
            $"Go to {productsPath} to see our products.");
    }
}

Šablony tras

Tokeny v rámci {} definují parametry trasy, které jsou vázané, pokud se trasa shoduje. V segmentu trasy je možné definovat více než jeden parametr trasy, ale parametry trasy musí být oddělené hodnotou literálu. Příklad:

{controller=Home}{action=Index}

není platná trasa, protože neexistuje žádná hodnota literálu mezi {controller} a {action}. Parametry trasy musí mít název a mohou mít zadané další atributy.

Literál jiný než směrovací parametry (například {id}) a oddělovač / cesty musí odpovídat textu v adrese URL. Porovnávání textu nerozlišuje malá a velká písmena a vychází z dekódované reprezentace cesty adresy URL. Chcete-li se shodovat s oddělovačem { parametru trasy literálu nebo }, uchytáte oddělovač opakováním znaku. Například {{ nebo }}.

Hvězdička * nebo dvojitá hvězdička **:

  • Lze použít jako předponu parametru trasy pro vazbu na rest identifikátor URI.
  • Nazývají se parametry catch-all. Příklad: blog/{**slug}
    • Odpovídá libovolnému identifikátoru blog/ URI, který začíná a má za ním libovolnou hodnotu.
    • Následující hodnota blog/ je přiřazena ke slug směrovací hodnotě.

Upozorňující

Parametr catch-all může nesprávně odpovídat trasám kvůli chybě při směrování. Aplikace ovlivněné touto chybou mají následující charakteristiky:

  • Například trasa pro zachytávání– vše {**slug}"
  • Trasa catch-all neodpovídá požadavkům, které by se měly shodovat.
  • Odebráním jiných tras začne fungovat zachytávání všech tras.

Podívejte se na chyby GitHubu 18677 a 16579 , například případy, které tuto chybu narazily.

Oprava výslovného souhlasu s touto chybou je obsažená v sadě .NET Core 3.1.301 SDK a novějších verzích. Následující kód nastaví interní přepínač, který tuto chybu opraví:

public static void Main(string[] args)
{
   AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", 
                         true);
   CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.

Parametry catch-all mohou také odpovídat prázdnému řetězci.

Parametr catch-all umisťuje příslušné znaky při použití trasy k vygenerování adresy URL, včetně znaků oddělovače / cest. Například trasa foo/{*path} s hodnotami { path = "my/path" } trasy generuje foo/my%2Fpath. Všimněte si řídicího lomítka. Pokud chcete znaky oddělovače cest odezvy použít předponu parametru ** trasy. Trasa foo/{**path} s { path = "my/path" } vygenerovanými .foo/my/path

Vzory adres URL, které se pokoušejí zachytit název souboru s volitelnou příponou souboru, mají další důležité informace. Představte si například šablonu files/{filename}.{ext?}. Když hodnoty obou filename hodnot existují ext , naplní se obě hodnoty. Pokud v adrese URL existuje jenom hodnota filename , trasa se shoduje, protože koncové hodnoty . jsou volitelné. Následující adresy URL odpovídají této trase:

  • /files/myFile.txt
  • /files/myFile

Parametry trasy můžou mít výchozí hodnoty určené zadáním výchozí hodnoty za názvem parametru odděleným symbolem rovná se (=). Například {controller=Home} definuje Home jako výchozí hodnotu pro controller. Výchozí hodnota se použije, pokud v adrese URL parametru není žádná hodnota. Parametry trasy jsou volitelné připojením otazníku (?) na konec názvu parametru. Například id?. Rozdíl mezi volitelnými hodnotami a výchozími parametry trasy je:

  • Parametr trasy s výchozí hodnotou vždy vytvoří hodnotu.
  • Volitelný parametr má hodnotu pouze v případě, že je hodnota poskytována adresou URL požadavku.

Parametry trasy můžou mít omezení, která musí odpovídat hodnotě trasy vázané z adresy URL. Přidání : a omezení názvu za názvem parametru trasy určuje vložené omezení parametru trasy. Pokud omezení vyžaduje argumenty, jsou za názvem omezení uzavřeny v závorkách (...) . Více vložených omezení lze zadat připojením jiného : názvu a názvu omezení.

Název omezení a argumenty se předávají službě IInlineConstraintResolver , aby se vytvořila instance IRouteConstraint , která se má použít při zpracování adresy URL. Například šablona blog/{article:minlength(10)} trasy určuje minlength omezení s argumentem 10. Další informace o omezeních tras a seznamu omezení poskytovaných architekturou najdete v části Omezení trasy.

Parametry trasy mohou mít také transformátory parametrů. Transformátory parametrů transformují hodnotu parametru při generování odkazů a odpovídajících akcí a stránek na adresy URL. Stejně jako omezení lze transformátory parametrů přidat do parametru trasy přidáním : názvu a transformátoru za název parametru trasy. Například šablona blog/{article:slugify} trasy určuje slugify transformátor. Další informace o transformátorech parametrů naleznete v části Transformátory parametrů .

Následující tabulka ukazuje ukázkové šablony tras a jejich chování:

Šablona trasy Příklad odpovídajícího identifikátoru URI Identifikátor URI požadavku...
hello /hello Odpovídá pouze jedné cestě /hello.
{Page=Home} / Odpovídá a nastaví Page na Home.
{Page=Home} /Contact Odpovídá a nastaví Page na Contact.
{controller}/{action}/{id?} /Products/List Mapuje se na Products kontroler a List akci.
{controller}/{action}/{id?} /Products/Details/123 Mapuje se Products na kontroler a Details akci s nastavenouid na hodnotu 123.
{controller=Home}/{action=Index}/{id?} / Mapuje se na kontroler a Index metoduHome. Vlastnost id je ignorována.
{controller=Home}/{action=Index}/{id?} /Products Mapuje se na kontroler a Index metoduProducts. Vlastnost id je ignorována.

Použití šablony je obecně nejjednodušším přístupem ke směrování. Omezení a výchozí hodnoty lze zadat také mimo šablonu trasy.

Komplexní segmenty

Komplexní segmenty se zpracovávají pomocí odpovídajících oddělovačů literálů zprava doleva způsobem, který není greedy . Jedná se například [Route("/a{b}c{d}")] o složitý segment. Složité segmenty fungují určitým způsobem, který je potřeba pochopit, aby je bylo možné úspěšně použít. Příklad v této části ukazuje, proč složité segmenty skutečně fungují dobře, když se text oddělovače nezobrazí uvnitř hodnot parametrů. Použití regulárního výrazu a ruční extrahování hodnot je potřeba pro složitější případy.

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Toto je souhrn kroků, které směrování provádí pomocí šablony /a{b}c{d} a cesty /abcdURL . Slouží | k vizualizaci fungování algoritmu:

  • První literál, zprava doleva, je c. Hledá se tedy /abcd zprava a najde /ab|c|d.
  • Vše napravo (d) se teď shoduje s parametrem {d}trasy .
  • Další literál, zprava doleva, je a. Takže /ab|c|d je prohledáno od místa, kde jsme skončili, pak a je nalezen /|a|b|c|d.
  • Hodnota vpravo (b) se teď shoduje s parametrem {b}trasy .
  • Neexistuje žádný zbývající text a žádná zbývající šablona trasy, takže se jedná o shodu.

Tady je příklad záporného případu, který používá stejnou šablonu /a{b}c{d} a cestu /aabcdURL . Slouží | k vizualizaci fungování algoritmu. Tento případ není shoda, která je vysvětlená stejným algoritmem:

  • První literál, zprava doleva, je c. Hledá se tedy /aabcd zprava a najde /aab|c|d.
  • Vše napravo (d) se teď shoduje s parametrem {d}trasy .
  • Další literál, zprava doleva, je a. Takže /aab|c|d je prohledáno od místa, kde jsme skončili, pak a je nalezen /a|a|b|c|d.
  • Hodnota vpravo (b) se teď shoduje s parametrem {b}trasy .
  • V tomto okamžiku existuje zbývající text a, ale algoritmus vyčeráhl šablonu trasy, aby parsovala, takže se nejedná o shodu.

Vzhledem k tomu, že odpovídající algoritmus není greedy:

  • Odpovídá nejmenšímu množství textu, který je možné v každém kroku.
  • Případ, kdy se hodnota oddělovače zobrazí uvnitř hodnot parametrů, způsobí, že se neshoduje.

Regulární výrazy poskytují mnohem větší kontrolu nad jejich odpovídajícím chováním.

Párování Greedy, označované také jako opožděné párování, odpovídá největšímu možnému řetězci. Greedy odpovídá nejmenšímu možnému řetězci.

Směrování se speciálními znaky

Směrování se speciálními znaky může vést k neočekávaným výsledkům. Představte si například kontroler s následující metodou akce:

[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null || todoItem.Name == null)
    {
        return NotFound();
    }

    return todoItem.Name;
}

Pokud string id obsahuje následující kódované hodnoty, mohou dojít k neočekávaným výsledkům:

ASCII Encoded
/ %2F
+

Parametry trasy nejsou vždy dekódovány adresou URL. Tento problém může být vyřešen v budoucnu. Další informace najdete v tomto problému na GitHubu.

Omezení trasy

Omezení směrování se provádějí, když došlo ke shodě s příchozí adresou URL a cesta url se tokenizuje do hodnot tras. Omezení tras obecně kontrolují hodnotu trasy přidruženou prostřednictvím šablony trasy a ověřte, jestli je hodnota přijatelná, nebo nepravdivá. Některá omezení trasy používají data mimo hodnotu trasy a zvažují, jestli je možné požadavek směrovat. Například HttpMethodRouteConstraint může přijmout nebo odmítnout požadavek na základě jeho příkazu HTTP. Omezení se používají v požadavcích směrování a generování propojení.

Upozorňující

Nepoužívejte omezení pro ověřování vstupu. Pokud se pro ověření vstupu používají omezení, výsledkem neplatného 404 vstupu je odpověď Nenalezena. Neplatný vstup by měl vytvořit chybný 400 požadavek s příslušnou chybovou zprávou. Omezení tras se používají k nejednoznačnosti podobných tras, nikoli k ověření vstupů pro konkrétní trasu.

Následující tabulka ukazuje ukázková omezení směrování a jejich očekávané chování:

omezení Příklad Příklady shod Notes
int {id:int} 123456789, -123456789 Odpovídá libovolnému celočíselnému číslu.
bool {active:bool} true, FALSE Shody true nebo false. Nerozlišují se malá a velká písmena
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Odpovídá platné DateTime hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
decimal {price:decimal} 49.99, -1,000.01 Odpovídá platné decimal hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
double {weight:double} 1.234, -1,001.01e8 Odpovídá platné double hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
float {weight:float} 1.234, -1,001.01e8 Odpovídá platné float hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Odpovídá platné Guid hodnotě.
long {ticks:long} 123456789, -123456789 Odpovídá platné long hodnotě.
minlength(value) {username:minlength(4)} Rick Řetězec musí mít alespoň 4 znaky.
maxlength(value) {filename:maxlength(8)} MyFile Řetězec nesmí být delší než 8 znaků.
length(length) {filename:length(12)} somefile.txt Řetězec musí mít přesně 12 znaků.
length(min,max) {filename:length(8,16)} somefile.txt Řetězec musí mít maximálně 8 znaků a nesmí být delší než 16 znaků.
min(value) {age:min(18)} 19 Celočíselná hodnota musí být alespoň 18.
max(value) {age:max(120)} 91 Celočíselná hodnota nesmí být větší než 120.
range(min,max) {age:range(18,120)} 91 Celočíselná hodnota musí být alespoň 18, ale nesmí být větší než 120.
alpha {name:alpha} Rick Řetězec musí obsahovat jeden nebo více abecedních znaků a-z a nerozlišuje velká a malá písmena.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 Řetězec musí odpovídat regulárnímu výrazu. Podívejte se na tipy k definování regulárního výrazu.
required {name:required} Rick Používá se k vynucení toho, aby během generování adresy URL byla přítomna hodnota, která není parametrem.

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Pro jeden parametr lze použít více omezení oddělených dvojtečkami. Například následující omezení omezuje parametr na celočíselnou hodnotu 1 nebo vyšší:

[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }

Upozorňující

Omezení směrování, která ověřují adresu URL a jsou převedeny na typ CLR, vždy používají neutrální jazykovou verzi. Například převod na typ int CLR nebo DateTime. Tato omezení předpokládají, že adresa URL není lokalizovatelná. Omezení tras poskytovaná architekturou nemění hodnoty uložené v hodnotách tras. Všechny hodnoty směrování parsované z adresy URL se ukládají jako řetězce. Omezení se například pokusí převést hodnotu trasy na hodnotu float, ale převedená hodnota se použije pouze k ověření, float že se dá převést na plovoucí hodnotu.

Regulární výrazy v omezeních

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Regulární výrazy lze zadat jako vložená omezení pomocí regex(...) omezení trasy. Metody v rodině MapControllerRoute také přijímají literál objektu omezení. Pokud se tento formulář použije, řetězcové hodnoty se interpretují jako regulární výrazy.

Následující kód používá omezení vložených regulárních výrazů:

app.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
    () => "Inline Regex Constraint Matched");

Následující kód používá literál objektu k určení omezení regulárního výrazu:

app.MapControllerRoute(
    name: "people",
    pattern: "people/{ssn}",
    constraints: new { ssn = "^\\d{3}-\\d{2}-\\d{4}$", },
    defaults: new { controller = "People", action = "List" });

Architektura ASP.NET Core přidává RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant do konstruktoru regulárních výrazů. Podívejte RegexOptions se na popis těchto členů.

Regulární výrazy používají oddělovače a tokeny podobné těm používaným směrováním a jazykem C#. Tokeny regulárních výrazů musí být uchycené. Chcete-li použít regulární výraz ^\d{3}-\d{2}-\d{4}$ v vložené omezení, použijte jednu z následujících možností:

  • Nahraďte \ znaky zadané v řetězci jako \\ znaky ve zdrojovém souboru jazyka C#, aby bylo možné řídicí znak řetězce utéct \ .
  • Doslovné řetězcové literály.

Chcete-li uvozovat znaky {oddělovače parametrů směrování , [}]zdvojnásobit znaky ve výrazu, {{například , }}, , . ]][[ Následující tabulka ukazuje regulární výraz a jeho řídicí verzi:

Regulární výraz Řídicí regulární výraz
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Regulární výrazy používané při směrování často začínají znakem ^ a odpovídají počáteční pozici řetězce. Výrazy často končí znakem $ a odpovídají konci řetězce. Znaky ^ a $ zajistěte, aby regulární výraz odpovídal celé hodnotě parametru trasy. ^ Bez znaků a $ znaků regulární výraz odpovídá jakémukoli podřetězci v řetězci, což je často nežádoucí. Následující tabulka obsahuje příklady a vysvětluje, proč se shodují nebo se neshodují:

Výraz String Párování Komentář
[a-z]{2} dobrý den Ano Shoda podřetěžce
[a-z]{2} 123abc456 Ano Shoda podřetěžce
[a-z]{2} mz Ano Odpovídá výrazu
[a-z]{2} MZ Ano Nerozlišuje se malá a velká písmena.
^[a-z]{2}$ dobrý den No Viz ^ a $ výše
^[a-z]{2}$ 123abc456 No Viz ^ a $ výše

Další informace o syntaxi regulárních výrazů naleznete v tématu Regulární výrazy rozhraní .NET Framework.

Chcete-li omezit parametr na známou sadu možných hodnot, použijte regulární výraz. Například {action:regex(^(list|get|create)$)} odpovídá pouze hodnotě action trasy do list, getnebo create. Pokud je řetězec předán do slovníku omezení, je řetězec ^(list|get|create)$ ekvivalentní. Omezení, která se předávají ve slovníku omezení, která neodpovídají některému ze známých omezení, se také považují za regulární výrazy. Omezení předaná v šabloně, která neodpovídají některému ze známých omezení, se považují za regulární výrazy.

Vlastní omezení trasy

Vlastní omezení trasy je možné vytvořit implementací IRouteConstraint rozhraní. Rozhraní IRouteConstraint obsahuje Match, který vrátí true , pokud je omezení splněno a false jinak.

Vlastní omezení trasy jsou zřídka nutná. Před implementací vlastního omezení trasy zvažte alternativy, jako je například vazba modelu.

Složka ASP.NET Core Constraints poskytuje dobré příklady vytváření omezení. Například GuidRouteConstraint.

Pokud chcete použít vlastní IRouteConstrainttyp omezení trasy, musí být zaregistrovaný v kontejneru služby v aplikaci ConstraintMap . A ConstraintMap je slovník, který mapuje klíče omezení směrování na IRouteConstraint implementace, které tato omezení ověřují. Aplikaci ConstraintMap je možné aktualizovat buď Program.cs v rámci AddRouting hovoru, nebo konfigurací RouteOptions přímo pomocí builder.Services.Configure<RouteOptions>. Příklad:

builder.Services.AddRouting(options =>
    options.ConstraintMap.Add("noZeroes", typeof(NoZeroesRouteConstraint)));

Předchozí omezení se použije v následujícím kódu:

[ApiController]
[Route("api/[controller]")]
public class NoZeroesController : ControllerBase
{
    [HttpGet("{id:noZeroes}")]
    public IActionResult Get(string id) =>
        Content(id);
}

Implementace NoZeroesRouteConstraint brání 0 použití v parametru trasy:

public class NoZeroesRouteConstraint : IRouteConstraint
{
    private static readonly Regex _regex = new(
        @"^[1-9]*$",
        RegexOptions.CultureInvariant | RegexOptions.IgnoreCase,
        TimeSpan.FromMilliseconds(100));

    public bool Match(
        HttpContext? httpContext, IRouter? route, string routeKey,
        RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (!values.TryGetValue(routeKey, out var routeValue))
        {
            return false;
        }

        var routeValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);

        if (routeValueString is null)
        {
            return false;
        }

        return _regex.IsMatch(routeValueString);
    }
}

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Předchozí kód:

  • Zabraňuje 0 v {id} segmentu trasy.
  • Ukazuje se, že poskytuje základní příklad implementace vlastního omezení. Neměla by se používat v produkční aplikaci.

Následující kód je lepším přístupem k tomu, aby se zabránilo zpracování obsahujícího id 0 :

[HttpGet("{id}")]
public IActionResult Get(string id)
{
    if (id.Contains('0'))
    {
        return StatusCode(StatusCodes.Status406NotAcceptable);
    }

    return Content(id);
}

Předchozí kód má oproti přístupu následující výhody NoZeroesRouteConstraint :

  • Nevyžaduje vlastní omezení.
  • Vrátí popisnější chybu, pokud parametr trasy obsahuje 0.

Transformátory parametrů

Transformátory parametrů:

Například vlastní slugify transformátor parametru ve vzoru blog\{article:slugify} trasy s Url.Action(new { article = "MyTestArticle" }) vygenerovanými blog\my-test-article.

Zvažte následující IOutboundParameterTransformer implementaci:

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string? TransformOutbound(object? value)
    {
        if (value is null)
        {
            return null;
        }

        return Regex.Replace(
            value.ToString()!,
                "([a-z])([A-Z])",
            "$1-$2",
            RegexOptions.CultureInvariant,
            TimeSpan.FromMilliseconds(100))
            .ToLowerInvariant();
    }
}

Chcete-li použít transformátor parametrů ve vzoru trasy, nakonfigurujte ho pomocí :ConstraintMap Program.cs

builder.Services.AddRouting(options =>
    options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer));

Architektura ASP.NET Core používá transformátory parametrů k transformaci identifikátoru URI, kde se koncový bod překládá. Například transformátory parametrů transformují směrovací hodnoty použité ke shodě area, controller, actiona page:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");

U předchozí šablony trasy se akce SubscriptionManagementController.GetAll shoduje s identifikátorem URI /subscription-management/get-all. Transformátor parametrů nemění směrovací hodnoty použité k vygenerování propojení. Například Url.Action("GetAll", "SubscriptionManagement") výstupy /subscription-management/get-all.

ASP.NET Core poskytuje konvence rozhraní API pro použití transformátorů parametrů se generovanými trasami:

Referenční informace ke generování adres URL

Tato část obsahuje odkaz na algoritmus implementovaný generováním adres URL. V praxi používají nejsložitější příklady generování adres URL kontrolery nebo Razor stránky. Další informace najdete v tématu Směrování v kontrolerů .

Proces generování adresy URL začíná voláním LinkGenerator.GetPathByAddress nebo podobnou metodou. Metoda je poskytována s adresou, sadou hodnot tras a volitelně informace o aktuálním požadavku z HttpContext.

Prvním krokem je použití adresy k vyřešení sady kandidátských koncových bodů pomocí typu IEndpointAddressScheme<TAddress> adresy.

Jakmile sada kandidátů najde schéma adres, koncové body se objednávají a zpracovávají iterativním způsobem, dokud operace generování adresy URL nebude úspěšná. Generování adresy URL nekontroluje nejednoznačnosti, první vrácený výsledek je konečný výsledek.

Řešení potíží s generováním adres URL pomocí protokolování

Prvním krokem při generování adresy URL při řešení potíží je nastavení úrovně Microsoft.AspNetCore.Routing protokolování na TRACE. LinkGenerator zaznamenává mnoho podrobností o jeho zpracování, které může být užitečné při řešení problémů.

Podrobnosti o generování adres URL najdete v referenčních informacích ke generování adres URL.

Adresy

Adresy jsou konceptem generování adres URL, které slouží k vytvoření vazby volání do generátoru odkazů na sadu kandidátských koncových bodů.

Adresy jsou rozšiřitelný koncept, který ve výchozím nastavení obsahuje dvě implementace:

  • Jako adresu použijte název koncového bodu (string):
    • Poskytuje podobné funkce jako název trasy MVC.
    • IEndpointNameMetadata Používá typ metadat.
    • Vyřeší zadaný řetězec s metadaty všech registrovaných koncových bodů.
    • Vyvolá výjimku při spuštění, pokud více koncových bodů používá stejný název.
    • Doporučuje se pro obecné použití mimo kontrolery a Razor stránky.
  • Použití hodnot směrování (RouteValuesAddress) jako adresy:
    • Poskytuje podobné funkce jako kontrolery a Razor starší generace adres URL stránky.
    • Velmi složité rozšířit a ladit.
    • Poskytuje implementaci, kterou IUrlHelperpoužívají pomocné rutiny značek, pomocné rutiny HTML, výsledky akcí atd.

Role schématu adres spočívá v přidružení mezi adresou a odpovídajícími koncovými body podle libovolných kritérií:

  • Schéma názvu koncového bodu provádí základní vyhledávání slovníku.
  • Schéma hodnot tras má komplexní nejlepší podmnožinu algoritmu sady.

Okolní hodnoty a explicitní hodnoty

Z aktuálního požadavku směrování přistupuje ke směrovacím hodnotám aktuálního požadavku HttpContext.Request.RouteValues. Hodnoty přidružené k aktuálnímu požadavku se označují jako okolní hodnoty. Pro účely srozumitelnosti se dokumentace týká směrovacích hodnot předávaných metodám jako explicitních hodnot.

Následující příklad ukazuje okolní hodnoty a explicitní hodnoty. Poskytuje okolní hodnoty z aktuálního požadavku a explicitních hodnot:

public class WidgetController : ControllerBase
{
    private readonly LinkGenerator _linkGenerator;

    public WidgetController(LinkGenerator linkGenerator) =>
        _linkGenerator = linkGenerator;

    public IActionResult Index()
    {
        var indexPath = _linkGenerator.GetPathByAction(
            HttpContext, values: new { id = 17 })!;

        return Content(indexPath);
    }

    // ...

Předchozí kód:

Následující kód poskytuje pouze explicitní hodnoty a žádné okolní hodnoty:

var subscribePath = _linkGenerator.GetPathByAction(
    "Subscribe", "Home", new { id = 17 })!;

Předchozí metoda vrátí /Home/Subscribe/17

Následující kód ve návratu WidgetController /Widget/Subscribe/17:

var subscribePath = _linkGenerator.GetPathByAction(
    HttpContext, "Subscribe", null, new { id = 17 });

Následující kód poskytuje kontroler z okolních hodnot v aktuálním požadavku a explicitní hodnoty:

public class GadgetController : ControllerBase
{
    public IActionResult Index() =>
        Content(Url.Action("Edit", new { id = 17 })!);
}

V předchozím kódu:

  • /Gadget/Edit/17 je vrácena.
  • UrlIUrlHelperzíská .
  • Action vygeneruje adresu URL s absolutní cestou pro metodu akce. Adresa URL obsahuje zadaný action název a route hodnoty.

Následující kód poskytuje okolní hodnoty z aktuálního požadavku a explicitní hodnoty:

public class IndexModel : PageModel
{
    public void OnGet()
    {
        var editUrl = Url.Page("./Edit", new { id = 17 });

        // ...
    }
}

Předchozí kód se nastaví url , když /Edit/17 Edit Razor Page obsahuje následující direktivu stránky:

@page "{id:int}"

Pokud stránka Upravit neobsahuje "{id:int}" šablonu trasy, url je /Edit?id=17.

Chování MVC IUrlHelper přidává kromě zde popsaných pravidel vrstvu složitosti:

  • IUrlHelper vždy poskytuje směrovací hodnoty z aktuálního požadavku jako okolní hodnoty.
  • IUrlHelper.Action vždy zkopíruje aktuální action hodnoty a controller směrovací hodnoty jako explicitní hodnoty, pokud je nepřepíše vývojář.
  • IUrlHelper.Page vždy zkopíruje aktuální page hodnotu trasy jako explicitní hodnotu, pokud ji nepřepíšete.
  • IUrlHelper.Page vždy přepíše aktuální handler hodnotu null trasy jako explicitní hodnoty, pokud ji nepřepíšete.

Uživatelé jsou často překvapeni chováním okolních hodnot, protože MVC zdánlivě nedodržuje vlastní pravidla. Z historických důvodů a z důvodu kompatibility mají určité hodnoty tras, jako actionje , controller, pagea handler mají vlastní zvláštní chování.

Ekvivalentní funkce poskytované LinkGenerator.GetPathByAction a LinkGenerator.GetPathByPage duplikuje tyto anomálie z důvodu kompatibility IUrlHelper .

Proces generování adres URL

Jakmile se najde sada kandidátských koncových bodů, algoritmus generování adres URL:

  • Zpracovává koncové body iterativním způsobem.
  • Vrátí první úspěšný výsledek.

Prvním krokem v tomto procesu je zneplatnění hodnoty trasy. Zneplatnění hodnoty trasy je proces, kterým směrování rozhodne, které hodnoty trasy z okolních hodnot se mají použít a které by se měly ignorovat. Každá okolní hodnota se považuje a buď v kombinaci s explicitními hodnotami, nebo je ignorována.

Nejlepším způsobem, jak přemýšlet o roli okolních hodnot, je, že se snaží uložit vývojáři aplikací psaní, v některých běžných případech. Scénáře, ve kterých jsou okolní hodnoty užitečné, se tradičně vztahují k MVC:

  • Při propojení s jinou akcí ve stejném řadiči není nutné zadat název kontroleru.
  • Při propojení s jiným řadičem ve stejné oblasti není nutné zadat název oblasti.
  • Při propojení se stejnou metodou akce není nutné zadávat hodnoty tras.
  • Při propojení s jinou částí aplikace nechcete přenášet hodnoty tras, které nemají v této části aplikace žádný význam.

Volání nebo LinkGenerator IUrlHelper vrácení jsou obvykle způsobená tím, že null nerozumí neplatné hodnotě trasy. Při řešení potíží s neplatnou hodnotou trasy můžete explicitně zadat více hodnot tras, abyste zjistili, jestli se tím problém vyřeší.

Neplatná hodnota trasy funguje na předpokladu, že schéma adresy URL aplikace je hierarchické s hierarchií vytvořenou zleva doprava. Představte si šablonu {controller}/{action}/{id?} směrování základního kontroleru, abyste získali intuitivní představu o tom, jak to funguje v praxi. Změna hodnoty zneplatní všechny směrovací hodnoty, které se zobrazují vpravo. To odráží předpoklad o hierarchii. Pokud má aplikace okolní hodnotu a idoperace určuje jinou hodnotu pro controller:

  • id nebude znovu použito, protože {controller} je nalevo od {id?}.

Několik příkladů demonstrujících tento princip:

  • Pokud explicitní hodnoty obsahují hodnotu pro id, okolní hodnota je id ignorována. Okolní hodnoty a controller action lze je použít.
  • Pokud explicitní hodnoty obsahují hodnotu pro action, je ignorována jakákoli okolní hodnota pro action . Lze použít okolní hodnoty controller . Pokud se explicitní hodnota pro jinou než okolní hodnota action actionpro , id hodnota se nepoužije. Pokud je explicitní hodnota action stejná jako okolní hodnota pro action, id lze použít hodnotu.
  • Pokud explicitní hodnoty obsahují hodnotu pro controller, je ignorována jakákoli okolní hodnota pro controller . Pokud se explicitní hodnota pro jinou než okolní hodnota controller controllerpro , action hodnoty a id hodnoty nebudou použity. Pokud je explicitní hodnota controller stejná jako okolní hodnota pro controller, action lze použít hodnoty a id hodnoty.

Tento proces je ještě složitější díky existenci tras atributů a vyhrazených konvenčních tras. Běžné trasy kontroleru, jako {controller}/{action}/{id?} je například určení hierarchie pomocí parametrů trasy. Pro vyhrazené konvenční trasy a trasy atributů pro kontrolery a Razor stránky:

  • Existuje hierarchie hodnot tras.
  • Nezobrazují se v šabloně.

V těchto případech generování adresy URL definuje koncept požadovaných hodnot . Koncové body vytvořené kontrolery a Razor stránkami mají zadané požadované hodnoty, které umožňují zneplatnění hodnoty trasy.

Podrobný algoritmus zneplatnění hodnoty trasy:

  • Požadované názvy hodnot se kombinují s parametry trasy a pak se zpracovávají zleva doprava.
  • Pro každý parametr se porovná okolní hodnota a explicitní hodnota:
    • Pokud je okolní hodnota a explicitní hodnota stejné, proces pokračuje.
    • Pokud je okolní hodnota přítomná a explicitní hodnota není, použije se při generování adresy URL okolní hodnota.
    • Pokud okolní hodnota není přítomná a explicitní hodnota je, zamítněte okolní hodnotu a všechny následné okolní hodnoty.
    • Pokud se nachází okolí a explicitní hodnota a obě hodnoty jsou odlišné, zamítněte okolní hodnotu a všechny následné okolní hodnoty.

V tuto chvíli je operace generování adres URL připravená k vyhodnocení omezení trasy. Sada přijatých hodnot se zkombinuje s výchozími hodnotami parametrů, které jsou k dispozici pro omezení. Pokud všechna omezení projdou, operace bude pokračovat.

Dále je možné použít akceptované hodnoty k rozšíření šablony trasy. Šablona trasy se zpracuje:

  • Zleva doprava.
  • Každý parametr má jeho přijatou hodnotu nahrazenou.
  • S následujícími zvláštními případy:
    • Pokud přijaté hodnoty chybí hodnota a parametr má výchozí hodnotu, použije se výchozí hodnota.
    • Pokud přijaté hodnoty chybí a parametr je nepovinný, zpracování pokračuje.
    • Pokud některý parametr trasy napravo od chybějícího volitelného parametru má hodnotu, operace selže.
    • Pokud je to možné, sbalí se souvislé parametry s výchozími hodnotami a volitelné parametry.

Hodnoty explicitně za předpokladu, že neodpovídají segmentu trasy, se přidají do řetězce dotazu. Následující tabulka ukazuje výsledek při použití šablony {controller}/{action}/{id?}trasy .

Okolní hodnoty Explicitní hodnoty Výsledek
controller = "Home" action = "O aplikaci" /Home/About
controller = "Home" controller = "Order", action = "About" /Order/About
controller = "Home", color = "Red" action = "O aplikaci" /Home/About
controller = "Home" action = "O produktu", barva = "Červená" /Home/About?color=Red

Volitelné pořadí parametrů trasy

Volitelné parametry trasy musí pocházet po všech požadovaných parametrech trasy. V následujícím kódu id name musí parametry pocházet za parametrem color :

using Microsoft.AspNetCore.Mvc;

namespace WebApplication1.Controllers;

[Route("api/[controller]")]
public class MyController : ControllerBase
{
    // GET /api/my/red/2/joe
    // GET /api/my/red/2
    // GET /api/my
    [HttpGet("{color}/{id:int?}/{name?}")]
    public IActionResult GetByIdAndOptionalName(string color, int id = 1, string? name = null)
    {
        return Ok($"{color} {id} {name ?? ""}");
    }
}

Problémy s neplatnou hodnotou trasy

Následující kód ukazuje příklad schématu generování adres URL, které není podporováno směrováním:

app.MapControllerRoute(
    "default",
    "{culture}/{controller=Home}/{action=Index}/{id?}");

app.MapControllerRoute(
    "blog",
    "{culture}/{**slug}",
    new { controller = "Blog", action = "ReadPost" });

V předchozím kódu culture se parametr trasy používá pro lokalizaci. Touha má parametr culture vždy přijmout jako okolní hodnotu. Parametr culture však není přijat jako okolní hodnota kvůli způsobu, jakým požadované hodnoty fungují:

  • "default" V šabloně culture trasy je parametr trasy vlevo od controller, takže změny controller nebudou cultureneplatné .
  • "blog" V šabloně culture trasy se parametr trasy považuje za napravo od controllerparametru trasy, který se zobrazí v požadovaných hodnotách.

Parsování cest URL pomocí LinkParser

Třída LinkParser přidává podporu analýzy cesty URL do sady hodnot tras. Metoda ParsePathByEndpointName přebírá název koncového bodu a cestu URL a vrátí sadu hodnot tras extrahovaných z cesty URL.

V následujícím příkladu kontroler akce GetProduct používá šablonu api/Products/{id} trasy a má Name :GetProduct

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}", Name = nameof(GetProduct))]
    public IActionResult GetProduct(string id)
    {
        // ...

Ve stejné třídě AddRelatedProduct kontroleru akce očekává cestu URL, pathToRelatedProductkterou lze poskytnout jako parametr řetězce dotazu:

[HttpPost("{id}/Related")]
public IActionResult AddRelatedProduct(
    string id, string pathToRelatedProduct, [FromServices] LinkParser linkParser)
{
    var routeValues = linkParser.ParsePathByEndpointName(
        nameof(GetProduct), pathToRelatedProduct);
    var relatedProductId = routeValues?["id"];

    // ...

V předchozím příkladu AddRelatedProduct akce extrahuje id hodnotu trasy z cesty URL. Například s cestou /api/Products/1URL , relatedProductId hodnota je nastavena na 1. Tento přístup umožňuje klientům rozhraní API používat cesty url při odkazování na prostředky, aniž by museli vědět, jak je taková adresa URL strukturovaná.

Konfigurace metadat koncového bodu

Následující odkazy obsahují informace o konfiguraci metadat koncového bodu:

Porovnávání hostitelů v trasách pomocí RequireHost

RequireHost použije omezení na trasu, která vyžaduje zadaného hostitele. Parametrem RequireHost [Host] může být:

  • Hostitel: www.domain.com, odpovídá www.domain.com libovolnému portu.
  • Hostitel se zástupným znakem: *.domain.com, shody www.domain.com, subdomain.domain.comnebo www.subdomain.domain.com na libovolném portu.
  • Port: *:5000Odpovídá portu 5000 s libovolným hostitelem.
  • Hostitel a port: www.domain.com:5000 nebo *.domain.com:5000odpovídá hostiteli a portu.

Lze zadat více parametrů pomocí RequireHost nebo [Host]. Omezení odpovídá hostitelům platným pro některý z parametrů. Například [Host("domain.com", "*.domain.com")] shody domain.com, www.domain.coma subdomain.domain.com.

Následující kód používá RequireHost k vyžadování zadaného hostitele na trase:

app.MapGet("/", () => "Contoso").RequireHost("contoso.com");
app.MapGet("/", () => "AdventureWorks").RequireHost("adventure-works.com");

app.MapHealthChecks("/healthz").RequireHost("*:8080");

Následující kód používá [Host] atribut na kontroleru k vyžadování některého ze zadaných hostitelů:

[Host("contoso.com", "adventure-works.com")]
public class HostsController : Controller
{
    public IActionResult Index() =>
        View();

    [Host("example.com")]
    public IActionResult Example() =>
        View();
}

[Host] Když se atribut použije na metodu kontroleru i akce:

  • Použije se atribut akce.
  • Atribut kontroleru je ignorován.

Skupiny tras

Metoda MapGroup rozšíření pomáhá uspořádat skupiny koncových bodů s běžnou předponou. Snižuje opakující se kód a umožňuje přizpůsobit celé skupiny koncových bodů jediným voláním metod, jako RequireAuthorization jsou a WithMetadata které přidávají metadata koncového bodu.

Například následující kód vytvoří dvě podobné skupiny koncových bodů:

app.MapGroup("/public/todos")
    .MapTodosApi()
    .WithTags("Public");

app.MapGroup("/private/todos")
    .MapTodosApi()
    .WithTags("Private")
    .AddEndpointFilterFactory(QueryPrivateTodos)
    .RequireAuthorization();


EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
    var dbContextIndex = -1;

    foreach (var argument in factoryContext.MethodInfo.GetParameters())
    {
        if (argument.ParameterType == typeof(TodoDb))
        {
            dbContextIndex = argument.Position;
            break;
        }
    }

    // Skip filter if the method doesn't have a TodoDb parameter.
    if (dbContextIndex < 0)
    {
        return next;
    }

    return async invocationContext =>
    {
        var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
        dbContext.IsPrivate = true;

        try
        {
            return await next(invocationContext);
        }
        finally
        {
            // This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
            dbContext.IsPrivate = false;
        }
    };
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
    group.MapGet("/", GetAllTodos);
    group.MapGet("/{id}", GetTodo);
    group.MapPost("/", CreateTodo);
    group.MapPut("/{id}", UpdateTodo);
    group.MapDelete("/{id}", DeleteTodo);

    return group;
}

V tomto scénáři můžete použít relativní adresu hlavičky Location ve výsledku 201 Created :

public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
    await database.AddAsync(todo);
    await database.SaveChangesAsync();

    return TypedResults.Created($"{todo.Id}", todo);
}

První skupina koncových bodů se bude shodovat pouze s požadavky s předponou /public/todos a jsou přístupná bez ověřování. Druhá skupina koncových bodů bude odpovídat pouze požadavkům s předponou /private/todos a vyžaduje ověření.

Objekt QueryPrivateTodos pro filtrování koncových bodů je místní funkce, která upravuje parametry obslužné rutiny TodoDb trasy tak, aby umožňovala přístup k privátním datům úkolů a jejich ukládání.

Skupiny tras také podporují vnořené skupiny a složité vzory předpon s parametry trasy a omezeními. V následujícím příkladu a obslužná rutina trasy namapovaná na user skupinu může zachytit {org} parametry a {group} parametry trasy definované v předponách vnější skupiny.

Předpona může být také prázdná. To může být užitečné pro přidání metadat koncového bodu nebo filtrů do skupiny koncových bodů beze změny vzoru trasy.

var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");

Přidání filtrů nebo metadat do skupiny se chová stejně jako jejich individuální přidání do každého koncového bodu před přidáním dalších filtrů nebo metadat, které mohly být přidány do vnitřní skupiny nebo konkrétního koncového bodu.

var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");

inner.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/inner group filter");
    return next(context);
});

outer.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/outer group filter");
    return next(context);
});

inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("MapGet filter");
    return next(context);
});

V předchozím příkladu vnější filtr zapíše příchozí požadavek před vnitřním filtrem, i když byl přidán druhý. Vzhledem k tomu, že filtry byly použity u různých skupin, pořadí, které byly přidány vzhledem k sobě, nezáleží. Přidají se filtry objednávek bez ohledu na to, jestli se použije na stejnou skupinu nebo konkrétní koncový bod.

Žádost o /outer/inner/ protokolování bude následující:

/outer group filter
/inner group filter
MapGet filter

Doprovodné materiály k výkonu pro směrování

Pokud má aplikace problémy s výkonem, směrování se často považuje za problém. Důvodem je podezření, že architektury, jako jsou kontrolery a Razor stránky, hlásí dobu strávenou uvnitř rozhraní v protokolovacích zprávách. Pokud je mezi časem hlášeným kontrolery a celkovým časem požadavku významný rozdíl:

  • Vývojáři eliminují kód aplikace jako zdroj problému.
  • Běžně se předpokládá, že příčinou je směrování.

Směrování je testované výkonem pomocí tisíců koncových bodů. Je nepravděpodobné, že by typická aplikace narazila na problém s výkonem jenom tím, že je příliš velká. Nejčastější hlavní příčinou pomalého směrování je obvykle špatně se chovající vlastní middleware.

Následující ukázka kódu ukazuje základní techniku zúžení zdroje zpoždění:

var logger = app.Services.GetRequiredService<ILogger<Program>>();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.UseRouting();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.UseAuthorization();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 3: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.MapGet("/", () => "Timing Test.");

Směrování podle času:

  • Prokládání každého middlewaru pomocí kopie middlewaru časování zobrazeného v předchozím kódu.
  • Přidejte jedinečný identifikátor pro korelaci dat časování s kódem.

Jedná se o základní způsob, jak zúžit zpoždění, pokud je významné, například více než 10ms. Odečítá Time 2 se od Time 1 sestav čas strávený v middlewaru UseRouting .

Následující kód používá kompaktnější přístup k předchozímu kódu časování:

public sealed class AutoStopwatch : IDisposable
{
    private readonly ILogger _logger;
    private readonly string _message;
    private readonly Stopwatch _stopwatch;
    private bool _disposed;

    public AutoStopwatch(ILogger logger, string message) =>
        (_logger, _message, _stopwatch) = (logger, message, Stopwatch.StartNew());

    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        _logger.LogInformation("{Message}: {ElapsedMilliseconds}ms",
            _message, _stopwatch.ElapsedMilliseconds);

        _disposed = true;
    }
}
var logger = app.Services.GetRequiredService<ILogger<Program>>();
var timerCount = 0;

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.UseRouting();

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.UseAuthorization();

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.MapGet("/", () => "Timing Test.");

Potenciálně nákladné funkce směrování

Následující seznam obsahuje přehled o funkcích směrování, které jsou relativně drahé v porovnání se základními šablonami tras:

  • Regulární výrazy: Je možné psát regulární výrazy, které jsou složité nebo mají dlouhou dobu trvání s malým množstvím vstupu.
  • Komplexní segmenty ({x}-{y}-{z}):
    • Jsou výrazně dražší než analýza běžného segmentu cesty URL.
    • Výsledkem je přidělení mnoha dalších podřetěžců.
  • Synchronní přístup k datům: Mnoho složitých aplikací má v rámci směrování přístup k databázi. Použijte body rozšiřitelnosti, například MatcherPolicy a EndpointSelectorContext, které jsou asynchronní.

Pokyny pro velké směrovací tabulky

Ve výchozím nastavení ASP.NET Core používá algoritmus směrování, který obchoduje s pamětí za čas procesoru. To má pěkný účinek, že čas porovnávání tras závisí pouze na délce cesty, která se má shodovat, a ne na počtu tras. Tento přístup ale může být v některých případech problematický, když má aplikace velký počet tras (v tisících) a v trasách existuje velké množství předpon proměnných. Například pokud trasy mají parametry v počátečních segmentech trasy, například {parameter}/some/literal.

Je nepravděpodobné, že by aplikace narazila na situaci, kdy se jedná o problém, pokud:

  • V aplikaci existuje velký počet tras, které tento model používají.
  • V aplikaci je velký počet tras.

Jak zjistit, jestli aplikace narazí na problém s velkou směrovací tabulkou

  • Existují dva příznaky, které je třeba hledat:
    • Aplikace se pomalu spustí při prvním požadavku.
      • Mějte na paměti, že je to povinné, ale nestačí. Existuje mnoho dalších problémů, které se nesměrují, než může způsobit pomalé spuštění aplikace. Zkontrolujte níže uvedený stav, abyste přesně zjistili, jestli aplikace v této situaci neběží.
    • Aplikace během spouštění spotřebovává velké množství paměti a výpis paměti zobrazuje velký počet Microsoft.AspNetCore.Routing.Matching.DfaNode instancí.

Jak tento problém vyřešit

U tras lze použít několik technik a optimalizací, které tento scénář z velké části zlepší:

  • Pokud je to možné, použijte omezení trasy na parametry, například {parameter:int}{parameter:guid}, {parameter:regex(\\d+)}, atd.
    • To umožňuje algoritmus směrování interně optimalizovat struktury používané pro porovnávání a výrazně snížit využitou paměť.
    • Ve většině případů to stačí, abyste se vrátili k přijatelnému chování.
  • Změňte trasy tak, aby se parametry přesunuly do pozdějších segmentů v šabloně.
    • Tím se sníží počet možných cest tak, aby odpovídal koncovému bodu dané cestě.
  • Použijte dynamickou trasu a dynamicky proveďte mapování na kontroler nebo stránku.
    • Toho lze dosáhnout pomocí MapDynamicControllerRoute a MapDynamicPageRoute.

Pokyny pro autory knihoven

Tato část obsahuje pokyny pro autory knihoven, kteří vycházejí ze směrování. Cílem těchto podrobností je zajistit, aby vývojáři aplikací měli dobré zkušenosti s používáním knihoven a architektur, které rozšiřují směrování.

Definování koncových bodů

Pokud chcete vytvořit architekturu, která používá směrování pro porovnávání adres URL, začněte definováním uživatelského prostředí, které je postavené na UseEndpoints.

DO staví na vrcholu IEndpointRouteBuilder. To umožňuje uživatelům vytvářet architekturu s jinými funkcemi ASP.NET Core bez nejasností. Každá šablona ASP.NET Core zahrnuje směrování. Předpokládejme, že směrování je pro uživatele k dispozici a známé.

// Your framework
app.MapMyFramework(...);

app.MapHealthChecks("/healthz");

DO vrátit zapečetěný beton typ z volání MapMyFramework(...) , které implementuje IEndpointConventionBuilder. Většina metod architektury Map... tento vzor dodržuje. Rozhraní IEndpointConventionBuilder :

  • Umožňuje složená metadata.
  • Cílí na různé rozšiřující metody.

Deklarování vlastního typu umožňuje do tvůrce přidat vlastní funkce specifické pro architekturu. Je v pořádku zabalit tvůrce deklarovaného architekturou a předat do něj volání.

// Your framework
app.MapMyFramework(...)
    .RequireAuthorization()
    .WithMyFrameworkFeature(awesome: true);

app.MapHealthChecks("/healthz");

ZVAŽTE psaní vlastního EndpointDataSource. EndpointDataSource je primitivní úroveň nízké úrovně pro deklarování a aktualizaci kolekce koncových bodů. EndpointDataSource je výkonné rozhraní API používané kontrolery a Razor stránkami.

Testy směrování mají základní příklad neaktualizuje zdroje dat.

ZVAŽTE implementaci GetGroupedEndpoints. Tím získáte úplnou kontrolu nad spouštěním konvencí skupin a konečnými metadaty ve seskupených koncových bodech. To například umožňuje vlastní EndpointDataSource implementaci spouštět filtry koncových bodů přidané do skupin.

VE výchozím nastavení se nepokoušejte EndpointDataSource o registraci. Vyžadovat, aby uživatelé zaregistrovali vaši architekturu v UseEndpoints. Filozofie směrování spočívá v tom, že ve výchozím nastavení se nic nezahrnuje a je to UseEndpoints místo pro registraci koncových bodů.

Vytvoření middlewaru integrovaného se směrováním

ZVAŽTE definování typů metadat jako rozhraní.

Umožňuje použít typy metadat jako atribut tříd a metod.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => true;
}

Architektury, jako jsou kontrolery a Razor stránky, podporují použití atributů metadat na typy a metody. Pokud deklarujete typy metadat:

  • Zpřístupní je jako atributy.
  • Většina uživatelů má zkušenosti s používáním atributů.

Deklarace typu metadat jako rozhraní přidává další vrstvu flexibility:

  • Rozhraní jsou kompozibilní.
  • Vývojáři můžou deklarovat své vlastní typy, které kombinují více zásad.

Umožňuje přepsat metadata, jak je znázorněno v následujícím příkladu:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SuppressCoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => false;
}

[CoolMetadata]
public class MyController : Controller
{
    public void MyCool() { }

    [SuppressCoolMetadata]
    public void Uncool() { }
}

Nejlepší způsob, jak postupovat podle těchto pokynů, je vyhnout se definování metadat značek:

  • Nehledáte jenom přítomnost typu metadat.
  • Definujte vlastnost metadat a zkontrolujte vlastnost.

Kolekce metadat je seřazená a podporuje přepsání podle priority. V případě kontrolerů jsou metadata pro metodu akce nejvýraznější.

Do make middleware užitečný s a bez směrování:

app.UseAuthorization(new AuthorizationPolicy() { ... });

// Your framework
app.MapMyFramework(...).RequireAuthorization();

Jako příklad tohoto návodu UseAuthorization zvažte middleware. Middleware pro autorizaci umožňuje předávat záložní zásady. Záložní zásada, pokud je zadaná, platí pro obě:

  • Koncové body bez zadané zásady
  • Požadavky, které neodpovídají koncovému bodu.

Díky tomu je middleware autorizace užitečný mimo kontext směrování. Autorizační middleware lze použít pro tradiční programování middlewaru.

Ladění diagnostiky

Podrobný výstup diagnostiky směrování nastavíte Logging:LogLevel:Microsoft na Debughodnotu . Ve vývojovém prostředí nastavte úroveň protokolu v appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

Další materiály

Směrování zodpovídá za porovnávání příchozích požadavků HTTP a odesílání těchto požadavků do spustitelných koncových bodů aplikace. Koncové body jsou jednotky spustitelného kódu zpracování požadavků. Koncové body se definují v aplikaci a konfigurují se při spuštění aplikace. Proces porovnávání koncových bodů může extrahovat hodnoty z adresy URL požadavku a poskytnout tyto hodnoty pro zpracování požadavků. Pomocí informací o koncovém bodu z aplikace je směrování také schopné generovat adresy URL, které se mapují na koncové body.

Aplikace můžou nakonfigurovat směrování pomocí:

Tento článek se zabývá podrobnostmi nízké úrovně směrování ASP.NET Core. Informace o konfiguraci směrování:

Základy směrování

Následující kód ukazuje základní příklad směrování:

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

app.MapGet("/", () => "Hello World!");

app.Run();

Předchozí příklad obsahuje jeden koncový bod pomocí MapGet metody:

  • Při odeslání požadavku HTTP GET na kořenovou adresu URL /:
    • Delegát požadavku se spustí.
    • Hello World! je zapsán do odpovědi HTTP.
  • Pokud metoda požadavku není GET nebo kořenová adresa URL není /, žádná trasa neodpovídá a vrátí se http 404.

Směrování používá dvojici middlewaru, zaregistrovaného uživatelem UseRouting a UseEndpoints:

  • UseRouting přidá trasu odpovídající kanálu middlewaru. Tento middleware se podívá na sadu koncových bodů definovaných v aplikaci a vybere nejlepší shodu na základě požadavku.
  • UseEndpoints přidá spuštění koncového bodu do kanálu middlewaru. Spustí delegáta přidruženého k vybranému koncovému bodu.

Aplikace obvykle nemusí volat UseRouting ani UseEndpoints. WebApplicationBuilder konfiguruje middlewarový kanál, který zabalí middleware přidaný do Program.cs UseRouting a UseEndpoints. Aplikace ale můžou změnit pořadí, ve kterém UseRouting se budou spouštět, UseEndpoints a to explicitním voláním těchto metod. Například následující kód provede explicitní volání UseRouting:

app.Use(async (context, next) =>
{
    // ...
    await next(context);
});

app.UseRouting();

app.MapGet("/", () => "Hello World!");

V předchozím kódu:

  • Volání pro app.Use registraci vlastního middlewaru, který se spouští na začátku kanálu.
  • Volání konfiguruje UseRouting middleware odpovídající směrování tak, aby se spustil po vlastním middlewaru.
  • Koncový bod zaregistrovaný na MapGet konci kanálu se spustí.

Pokud by předchozí příklad nezahrnoval volání UseRouting, vlastní middleware by se spustil za middlewarem odpovídajícím trasě.

Koncové body

Metoda MapGet se používá k definování koncového bodu. Koncový bod je něco, co může být:

  • Vybráno tak, že se shoduje s adresou URL a metodou HTTP.
  • Spustí se spuštěním delegáta.

Koncové body, které se dají spárovat a spouštět aplikací, jsou nakonfigurované v UseEndpoints. Například MapGet, MapPosta podobné metody připojit žádosti delegáty do systému směrování. Další metody lze použít k připojení funkcí architektury ASP.NET Core ke směrovacímu systému:

Následující příklad ukazuje směrování s sofistikovanější šablonou trasy:

app.MapGet("/hello/{name:alpha}", (string name) => $"Hello {name}!");

Řetězec /hello/{name:alpha} je šablona trasy. Ke konfiguraci shody koncového bodu se používá šablona trasy. V tomto případě šablona odpovídá:

  • Adresa URL, jako je /hello/Docs
  • Všechny cesty URL, které začínají /hello/ posloupností abecedních znaků. :alpha použije omezení trasy, které odpovídá pouze abecedním znakům. Omezení tras jsou vysvětlena dále v tomto článku.

Druhý segment cesty URL: {name:alpha}

Následující příklad ukazuje směrování s kontrolami stavu a autorizací:

app.UseAuthentication();
app.UseAuthorization();

app.MapHealthChecks("/healthz").RequireAuthorization();
app.MapGet("/", () => "Hello World!");

Předchozí příklad ukazuje, jak:

  • S směrováním je možné použít autorizační middleware.
  • Koncové body je možné použít ke konfiguraci chování autorizace.

Volání MapHealthChecks přidá koncový bod kontroly stavu. Při zřetězování RequireAuthorization k tomuto volání se ke koncovému bodu připojí zásady autorizace.

Volání UseAuthentication a UseAuthorization přidání middlewaru pro ověřování a autorizaci Tyto middleware jsou umístěny mezi UseRouting a UseEndpoints tak, aby mohly:

  • Podívejte se, který koncový bod vybral UseRouting.
  • Před odesláním do koncového bodu použijte zásadu UseEndpoints autorizace.

Metadata koncového bodu

V předchozím příkladu existují dva koncové body, ale k autorizační zásadě jsou připojené jenom koncové body kontroly stavu. Pokud požadavek odpovídá koncovému bodu kontroly stavu, /healthzprovede se kontrola autorizace. To ukazuje, že koncové body můžou mít připojená další data. Tato další data se nazývají metadata koncového bodu:

  • Metadata je možné zpracovat pomocí middlewaru pracujícího se směrováním.
  • Metadata můžou být libovolného typu .NET.

Koncepty směrování

Systém směrování vychází z kanálu middlewaru přidáním výkonného konceptu koncového bodu . Koncové body představují jednotky funkcí aplikace, které se vzájemně liší z hlediska směrování, autorizace a libovolného počtu systémů ASP.NET Core.

Definice koncového bodu ASP.NET Core

Koncový bod ASP.NET Core je:

Následující kód ukazuje, jak načíst a zkontrolovat koncový bod odpovídající aktuálnímu požadavku:

app.Use(async (context, next) =>
{
    var currentEndpoint = context.GetEndpoint();

    if (currentEndpoint is null)
    {
        await next(context);
        return;
    }

    Console.WriteLine($"Endpoint: {currentEndpoint.DisplayName}");

    if (currentEndpoint is RouteEndpoint routeEndpoint)
    {
        Console.WriteLine($"  - Route Pattern: {routeEndpoint.RoutePattern}");
    }

    foreach (var endpointMetadata in currentEndpoint.Metadata)
    {
        Console.WriteLine($"  - Metadata: {endpointMetadata}");
    }

    await next(context);
});

app.MapGet("/", () => "Inspect Endpoint.");

Koncový bod, pokud je vybraný, lze načíst z objektu HttpContext. Jeho vlastnosti je možné zkontrolovat. Objekty koncového bodu jsou neměnné a po vytvoření není možné je změnit. Nejběžnějším typem RouteEndpointkoncového bodu je . RouteEndpoint obsahuje informace, které mu umožní vybrat směrovací systém.

V předchozím kódu aplikace. Slouží ke konfiguraci vloženého middlewaru.

Následující kód ukazuje, že v závislosti na tom, kde app.Use se v kanálu volá, nemusí existovat koncový bod:

// Location 1: before routing runs, endpoint is always null here.
app.Use(async (context, next) =>
{
    Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

app.UseRouting();

// Location 2: after routing runs, endpoint will be non-null if routing found a match.
app.Use(async (context, next) =>
{
    Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

// Location 3: runs when this endpoint matches
app.MapGet("/", (HttpContext context) =>
{
    Console.WriteLine($"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    return "Hello World!";
}).WithDisplayName("Hello");

app.UseEndpoints(_ => { });

// Location 4: runs after UseEndpoints - will only run if there was no match.
app.Use(async (context, next) =>
{
    Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    await next(context);
});

Předchozí ukázka přidá Console.WriteLine příkazy, které zobrazují, jestli byl vybrán koncový bod nebo ne. Pro přehlednost ukázka přiřadí zadaný koncový bod zobrazovaný název / .

Předchozí ukázka také zahrnuje volání UseRouting a UseEndpoints řízení přesně toho, kdy se tyto middlewary spouští v rámci kanálu.

Spuštění tohoto kódu s adresou URL / zobrazení:

1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello

Spuštění tohoto kódu s jakoukoli jinou adresou URL zobrazí:

1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)

Tento výstup ukazuje, že:

  • Koncový bod je před zavolání vždy null UseRouting .
  • Pokud se najde shoda, koncový bod je mezi UseRouting a UseEndpoints.
  • Middleware UseEndpoints je terminál , když se najde shoda. Middleware terminálu je definován dále v tomto článku.
  • Middleware po spuštění pouze v UseEndpoints případě, že nebyla nalezena žádná shoda.

Middleware UseRouting používá metodu SetEndpoint pro připojení koncového bodu k aktuálnímu kontextu. Middleware je možné nahradit UseRouting vlastní logikou a přesto získat výhody používání koncových bodů. Koncové body jsou primitivní nízké úrovně, jako je middleware, a nejsou svázané s implementací směrování. Většina aplikací nemusí nahradit UseRouting vlastní logikou.

Middleware UseEndpoints je navržený tak, aby se používal společně s middlewarem UseRouting . Základní logika pro spuštění koncového bodu není složitá. Slouží GetEndpoint k načtení koncového bodu a následnému vyvolání jeho RequestDelegate vlastnosti.

Následující kód ukazuje, jak může middleware ovlivnit nebo reagovat na směrování:

app.UseHttpMethodOverride();
app.UseRouting();

app.Use(async (context, next) =>
{
    if (context.GetEndpoint()?.Metadata.GetMetadata<RequiresAuditAttribute>() is not null)
    {
        Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
    }

    await next(context);
});

app.MapGet("/", () => "Audit isn't required.");
app.MapGet("/sensitive", () => "Audit required for sensitive data.")
    .WithMetadata(new RequiresAuditAttribute());
public class RequiresAuditAttribute : Attribute { }

Předchozí příklad ukazuje dva důležité koncepty:

  • Middleware se může spustit před UseRouting úpravou dat, se kterými směrování pracuje.
  • Middleware se může spustit mezi UseRouting směrováním a UseEndpoints zpracovat výsledky směrování před spuštěním koncového bodu.
    • Middleware, který běží mezi UseRouting a UseEndpoints:
      • Obvykle kontroluje metadata, aby porozuměla koncovým bodům.
      • Často provádí rozhodnutí o zabezpečení, jak to dělá UseAuthorization a UseCors.
    • Kombinace middlewaru a metadat umožňuje konfigurovat zásady pro jednotlivé koncové body.

Předchozí kód ukazuje příklad vlastního middlewaru, který podporuje zásady pro jednotlivé koncové body. Middleware zapíše protokol auditu přístupu k citlivým datům do konzoly. Middleware je možné nakonfigurovat tak, aby auditoval koncový bod s RequiresAuditAttribute metadaty. Tato ukázka ukazuje způsob vyjádření výslovného souhlasu, kdy se auditují jenom koncové body označené jako citlivé. Tuto logiku je možné definovat obráceně, auditovat všechno, co není označené jako bezpečné, například. Systém metadat koncového bodu je flexibilní. Tato logika by mohla být navržena jakýmkoli způsobem, který vyhovuje případu použití.

Předchozí vzorový kód je určený k předvedení základních konceptů koncových bodů. Ukázka není určená pro produkční použití. Ucelenější verze middlewaru protokolu auditu:

  • Přihlaste se k souboru nebo databázi.
  • Uveďte podrobnosti, jako je uživatel, IP adresa, název citlivého koncového bodu a další.

Metadata zásad auditu jsou definována RequiresAuditAttribute jako Attribute jednodušší použití s architekturami založenými na třídách, jako jsou kontrolery a SignalR. Při použití trasy ke kódu:

  • Metadata jsou připojena pomocí rozhraní API tvůrce.
  • Architektury založené na třídách zahrnují všechny atributy odpovídající metody a třídy při vytváření koncových bodů.

Osvědčenými postupy pro typy metadat je definovat buď jako rozhraní, nebo atributy. Rozhraní a atributy umožňují opakované použití kódu. Systém metadat je flexibilní a neukládá žádná omezení.

Porovnání middlewaru terminálu se směrováním

Následující příklad ukazuje terminálový middleware i směrování:

// Approach 1: Terminal Middleware.
app.Use(async (context, next) =>
{
    if (context.Request.Path == "/")
    {
        await context.Response.WriteAsync("Terminal Middleware.");
        return;
    }

    await next(context);
});

app.UseRouting();

// Approach 2: Routing.
app.MapGet("/Routing", () => "Routing.");

Styl middlewaru zobrazeného s terminálovým middlewarem Approach 1: . Říká se tomu middleware terminálu, protože dělá odpovídající operaci:

  • Odpovídající operace v předchozí ukázce je Path == "/" pro middleware a Path == "/Routing" pro směrování.
  • Když je shoda úspěšná, spustí některé funkce a vrátí se místo vyvolání middlewaru next .

Říká se tomu middleware terminálu, protože ukončí hledání, spustí některé funkce a vrátí se.

Následující seznam porovnává middleware terminálu se směrováním:

  • Oba přístupy umožňují ukončení kanálu zpracování:
    • Middleware ukončí kanál tím, že se místo vyvolání next.
    • Koncové body jsou vždy terminálové.
  • Middleware terminálu umožňuje umístění middlewaru na libovolné místo v kanálu:
  • Middleware terminálu umožňuje libovolnému kódu určit, kdy middleware odpovídá:
    • Vlastní kód pro porovnávání tras může být podrobný a obtížně zapisuje správně.
    • Směrování poskytuje jednoduchá řešení pro typické aplikace. Většina aplikací nevyžaduje vlastní kód pro porovnávání tras.
  • Rozhraní koncových bodů s middlewarem, jako UseAuthorization je například a UseCors.
    • Použití middlewaru terminálu s UseAuthorization autorizačním systémem nebo UseCors vyžaduje ruční propojení se systémem autorizace.

Koncový bod definuje obojí:

  • Delegát na zpracování požadavků.
  • Kolekce libovolných metadat. Metadata se používají k implementaci průřezových aspektů na základě zásad a konfigurace připojených k jednotlivým koncovým bodům.

Terminálový middleware může být efektivní nástroj, ale může vyžadovat:

  • Značné množství kódování a testování.
  • Ruční integrace s jinými systémy za účelem dosažení požadované úrovně flexibility.

Před zápisem middlewaru terminálu zvažte integraci se směrováním.

Existující middleware terminálu, který se integruje s mapou , nebo MapWhen se obvykle dá převést na koncový bod podporující směrování. MapHealthChecks ukazuje vzor pro router-ware:

Následující kód ukazuje použití MapHealthChecks:

app.UseAuthentication();
app.UseAuthorization();

app.MapHealthChecks("/healthz").RequireAuthorization();

Předchozí ukázka ukazuje, proč je vrácení objektu tvůrce důležité. Vrácení objektu tvůrce umožňuje vývojáři aplikací konfigurovat zásady, jako je autorizace pro koncový bod. V tomto příkladu nemá middleware kontroly stavu žádnou přímou integraci se systémem autorizace.

Systém metadat byl vytvořen v reakci na problémy zjištěné autory rozšiřitelnosti pomocí middlewaru terminálu. Pro každý middleware je problematické implementovat vlastní integraci s autorizačním systémem.

Porovnávání adres URL

  • Je proces, kterým směrování odpovídá příchozímu požadavku na koncový bod.
  • Je založená na datech v cestě URL a hlavičkách.
  • Můžete ho rozšířit, abyste zvážili všechna data v požadavku.

Když se spustí middleware směrování, nastaví Endpoint hodnotu požadavku na funkci HttpContext požadavku z aktuálního požadavku:

Middleware se spustí po spuštění middlewaru směrování, který může zkontrolovat koncový bod a provést akci. Například autorizační middleware může probrat kolekci metadat koncového bodu pro zásady autorizace. Po spuštění veškerého middlewaru v kanálu zpracování požadavků se vyvolá delegát vybraného koncového bodu.

Systém směrování ve směrování koncového bodu zodpovídá za veškerá rozhodnutí o odesílání. Vzhledem k tomu, že middleware používá zásady na základě vybraného koncového bodu, je důležité, aby:

  • Jakékoli rozhodnutí, které může ovlivnit odesílání nebo použití zásad zabezpečení, se provádí v rámci systému směrování.

Upozorňující

V případě zpětné kompatibility se při spuštění delegáta koncového RouteContext.RouteData bodu kontroleru nebo Razor stránky nastaví vlastnosti na odpovídající hodnoty na základě dosud provedeného zpracování požadavku.

Typ RouteContext bude v budoucí verzi označen jako zastaralý:

  • Migrovat RouteData.Values na HttpRequest.RouteValues.
  • Migrace RouteData.DataTokens pro načtení IDataTokensMetadata z metadat koncového bodu

Porovnávání adres URL funguje v konfigurovatelné sadě fází. V každé fázi je výstup sadou shod. Sadu shod lze dále zúžit v další fázi. Implementace směrování nezaručuje pořadí zpracování pro odpovídající koncové body. Všechny možné shody se zpracovávají najednou. Fáze porovnávání adres URL probíhají v následujícím pořadí. ASP.NET Core:

  1. Zpracovává cestu URL vůči sadě koncových bodů a jejich šablon tras a shromažďuje všechny shody.
  2. Vezme předchozí seznam a odebere shody, které selžou s použitými omezeními trasy.
  3. Vezme předchozí seznam a odebere shody, které sadu instancí selžou MatcherPolicy .
  4. EndpointSelector Použije k poslednímu rozhodnutí z předchozího seznamu.

Seznam koncových bodů má prioritu podle následujících:

Všechny odpovídající koncové body se zpracovávají v každé fázi, dokud se nedosáhne EndpointSelector . Jedná se EndpointSelector o konečnou fázi. Jako nejlepší shodu zvolí koncový bod s nejvyšší prioritou. Pokud existují jiné shody se stejnou prioritou jako nejlepší shoda, vyvolá se výjimka nejednoznačné shody.

Priorita trasy se vypočítá na základě konkrétnější šablony trasy, která má vyšší prioritu. Představte si například šablony /hello a /{message}:

  • Obě odpovídají cestě /helloURL .
  • /hello je konkrétnější a proto vyšší priorita.

Obecně platí, že priorita trasy má dobrou úlohu při výběru nejvhodnější shody pro druhy schémat adres URL používaných v praxi. Používejte Order pouze v případě potřeby, abyste se vyhnuli nejednoznačnosti.

Vzhledem k druhům rozšiřitelnosti poskytované směrováním není možné, aby směrovací systém předem počítaly nejednoznačné trasy. Představte si příklad, jako jsou šablony /{message:alpha} tras a /{message:int}:

  • Omezení alpha odpovídá pouze abecedním znakům.
  • Omezení int odpovídá pouze číslu.
  • Tyto šablony mají stejnou prioritu tras, ale obě adresy URL se neshodují.
  • Pokud systém směrování nahlásil při spuštění nejednoznačnosti, zablokoval by tento platný případ použití.

Upozorňující

Pořadí operací uvnitř UseEndpoints nemá vliv na chování směrování, s jednou výjimkou. MapControllerRoute a MapAreaRoute automaticky přiřadí hodnotu objednávky ke svým koncovým bodům na základě pořadí, ve které jsou vyvolány. To simuluje dlouhodobé chování kontrolerů bez systému směrování, který poskytuje stejné záruky jako starší implementace směrování.

Směrování koncových bodů v ASP.NET Core:

  • Nemá koncept tras.
  • Neposkytuje záruky objednávání. Všechny koncové body se zpracovávají najednou.

Priorita šablony trasy a pořadí výběru koncového bodu

Priorita šablony trasy je systém, který každé šabloně trasy přiřadí hodnotu na základě toho, jak je konkrétní. Priorita šablony trasy:

  • Vyhne se nutnosti upravovat pořadí koncových bodů v běžných případech.
  • Snaží se shodovat s očekáváními obecného chování směrování.

Představte si například šablony /Products/List a /Products/{id}. Bylo by rozumné předpokládat, že /Products/List je lepší shoda než /Products/{id} pro cestu /Products/ListURL . To funguje, protože literálový segment /List je považován za lepší prioritu než segment /{id}parametru .

Podrobnosti o tom, jak funguje priorita, jsou svázány s tím, jak se definují šablony tras:

  • Šablony s více segmenty jsou považovány za konkrétnější.
  • Segment s literálovým textem se považuje za konkrétnější než segment parametru.
  • Segment parametru s omezením se považuje za konkrétnější než jeden bez.
  • Složitý segment se považuje za specifický jako segment parametru s omezením.
  • Parametry pro zachytávání jsou nejmíň specifické. Důležité informace o trasách pro zachytávání všech tras najdete v části Šablony tras catch-all.

Koncepty generování adres URL

Generování adres URL:

  • Je proces, pomocí kterého směrování může vytvořit cestu URL na základě sady hodnot tras.
  • Umožňuje logické oddělení mezi koncovými body a adresami URL, které k nim přistupují.

Směrování koncových bodů zahrnuje LinkGenerator rozhraní API. LinkGenerator je jednoúčelová služba, která je k dispozici z DI. Rozhraní LinkGenerator API se dá použít mimo kontext spuštěného požadavku. Mvc.IUrlHelper a scénáře, které se spoléhají , IUrlHelperjako jsou pomocné rutiny značek, pomocné rutiny HTML a výsledky akcí, používají LinkGenerator rozhraní API interně k poskytování možností generování odkazů.

Generátor odkazů je podporován konceptem adresního a adresního schématu. Schéma adres je způsob, jak určit koncové body, které by se měly zvážit pro generování propojení. Například scénáře názvu trasy a směrovacích hodnot, které mnoho uživatelů znáte z kontrolerů a Razor Stránky se implementují jako schéma adres.

Generátor odkazů může odkazovat na kontrolery a Razor stránky pomocí následujících rozšiřujících metod:

Přetížení těchto metod přijímají argumenty, které zahrnují HttpContext. Tyto metody jsou funkčně ekvivalentní url.Action a Url.Page, ale nabízejí další flexibilitu a možnosti.

Metody GetPath* jsou nejvíce podobné Url.Action a Url.Pagev tom, že vygenerují identifikátor URI obsahující absolutní cestu. Metody GetUri* vždy generují absolutní identifikátor URI obsahující schéma a hostitele. Metody, které přijímají HttpContext vygenerování identifikátoru URI v kontextu prováděcího požadavku. Hodnoty okolní trasy, základní cesta URL, schéma a hostitel ze spuštěného požadavku se použijí, pokud je nepřepíšete.

LinkGenerator je volána s adresou. Generování identifikátoru URI probíhá ve dvou krocích:

  1. Adresa je svázaná se seznamem koncových bodů, které odpovídají adrese.
  2. Vyhodnocuje se RoutePattern každý koncový bod, dokud se nenajde vzor trasy, který odpovídá zadaným hodnotám. Výsledný výstup se zkombinuje s ostatními částmi identifikátoru URI zadanými do generátoru propojení a vrátí se.

Metody poskytované standardními možnostmi LinkGenerator generování propojení pro libovolný typ adresy. Nejpohodlnější způsob použití generátoru propojení je prostřednictvím rozšiřujících metod, které provádějí operace pro konkrétní typ adresy:

Extension – metoda Popis
GetPathByAddress Vygeneruje identifikátor URI s absolutní cestou na základě zadaných hodnot.
GetUriByAddress Vygeneruje absolutní identifikátor URI na základě zadaných hodnot.

Upozorňující

Věnujte pozornost následujícím důsledkům volání LinkGenerator metod:

  • V konfiguraci aplikace používejte GetUri* metody rozšíření s opatrností, která neověřuje hlavičku Host příchozích požadavků. Pokud hlavička Host příchozích požadavků není ověřená, může být nedůvěryhodný vstup požadavku odeslán zpět klientovi v identifikátorech URI v zobrazení nebo stránce. Doporučujeme, aby všechny produkční aplikace nakonfigurovaly svůj server tak, aby ověřovaly hlavičku Host proti známým platným hodnotám.

  • Používejte LinkGenerator s opatrností v middlewaru v kombinaci s Map nebo MapWhen. Map* změní základní cestu prováděcího požadavku, která má vliv na výstup generování propojení. LinkGenerator Všechna rozhraní API umožňují zadat základní cestu. Zadejte prázdnou Map* základní cestu, která vrátí zpět vliv na generování propojení.

Příklad middlewaru

V následujícím příkladu middleware používá LinkGenerator rozhraní API k vytvoření odkazu na metodu akce, která obsahuje seznam produktů pro ukládání. Použití generátoru odkazů vložením do třídy a volání GenerateLink je k dispozici pro libovolnou třídu v aplikaci:

public class ProductsMiddleware
{
    private readonly LinkGenerator _linkGenerator;

    public ProductsMiddleware(RequestDelegate next, LinkGenerator linkGenerator) =>
        _linkGenerator = linkGenerator;

    public async Task InvokeAsync(HttpContext httpContext)
    {
        httpContext.Response.ContentType = MediaTypeNames.Text.Plain;

        var productsPath = _linkGenerator.GetPathByAction("Products", "Store");

        await httpContext.Response.WriteAsync(
            $"Go to {productsPath} to see our products.");
    }
}

Šablony tras

Tokeny v rámci {} definují parametry trasy, které jsou vázané, pokud se trasa shoduje. V segmentu trasy je možné definovat více než jeden parametr trasy, ale parametry trasy musí být oddělené hodnotou literálu. Příklad:

{controller=Home}{action=Index}

není platná trasa, protože neexistuje žádná hodnota literálu mezi {controller} a {action}. Parametry trasy musí mít název a mohou mít zadané další atributy.

Literál jiný než směrovací parametry (například {id}) a oddělovač / cesty musí odpovídat textu v adrese URL. Porovnávání textu nerozlišuje malá a velká písmena a vychází z dekódované reprezentace cesty adresy URL. Chcete-li se shodovat s oddělovačem { parametru trasy literálu nebo }, uchytáte oddělovač opakováním znaku. Například {{ nebo }}.

Hvězdička * nebo dvojitá hvězdička **:

  • Lze použít jako předponu parametru trasy pro vazbu na rest identifikátor URI.
  • Nazývají se parametry catch-all. Příklad: blog/{**slug}
    • Odpovídá libovolnému identifikátoru blog/ URI, který začíná a má za ním libovolnou hodnotu.
    • Následující hodnota blog/ je přiřazena ke slug směrovací hodnotě.

Upozorňující

Parametr catch-all může nesprávně odpovídat trasám kvůli chybě při směrování. Aplikace ovlivněné touto chybou mají následující charakteristiky:

  • Například trasa pro zachytávání– vše {**slug}"
  • Trasa catch-all neodpovídá požadavkům, které by se měly shodovat.
  • Odebráním jiných tras začne fungovat zachytávání všech tras.

Podívejte se na chyby GitHubu 18677 a 16579 , například případy, které tuto chybu narazily.

Oprava výslovného souhlasu s touto chybou je obsažená v sadě .NET Core 3.1.301 SDK a novějších verzích. Následující kód nastaví interní přepínač, který tuto chybu opraví:

public static void Main(string[] args)
{
   AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", 
                         true);
   CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.

Parametry catch-all mohou také odpovídat prázdnému řetězci.

Parametr catch-all umisťuje příslušné znaky při použití trasy k vygenerování adresy URL, včetně znaků oddělovače / cest. Například trasa foo/{*path} s hodnotami { path = "my/path" } trasy generuje foo/my%2Fpath. Všimněte si řídicího lomítka. Pokud chcete znaky oddělovače cest odezvy použít předponu parametru ** trasy. Trasa foo/{**path} s { path = "my/path" } vygenerovanými .foo/my/path

Vzory adres URL, které se pokoušejí zachytit název souboru s volitelnou příponou souboru, mají další důležité informace. Představte si například šablonu files/{filename}.{ext?}. Když hodnoty obou filename hodnot existují ext , naplní se obě hodnoty. Pokud v adrese URL existuje jenom hodnota filename , trasa se shoduje, protože koncové hodnoty . jsou volitelné. Následující adresy URL odpovídají této trase:

  • /files/myFile.txt
  • /files/myFile

Parametry trasy můžou mít výchozí hodnoty určené zadáním výchozí hodnoty za názvem parametru odděleným symbolem rovná se (=). Například {controller=Home} definuje Home jako výchozí hodnotu pro controller. Výchozí hodnota se použije, pokud v adrese URL parametru není žádná hodnota. Parametry trasy jsou volitelné připojením otazníku (?) na konec názvu parametru. Například id?. Rozdíl mezi volitelnými hodnotami a výchozími parametry trasy je:

  • Parametr trasy s výchozí hodnotou vždy vytvoří hodnotu.
  • Volitelný parametr má hodnotu pouze v případě, že je hodnota poskytována adresou URL požadavku.

Parametry trasy můžou mít omezení, která musí odpovídat hodnotě trasy vázané z adresy URL. Přidání : a omezení názvu za názvem parametru trasy určuje vložené omezení parametru trasy. Pokud omezení vyžaduje argumenty, jsou za názvem omezení uzavřeny v závorkách (...) . Více vložených omezení lze zadat připojením jiného : názvu a názvu omezení.

Název omezení a argumenty se předávají službě IInlineConstraintResolver , aby se vytvořila instance IRouteConstraint , která se má použít při zpracování adresy URL. Například šablona blog/{article:minlength(10)} trasy určuje minlength omezení s argumentem 10. Další informace o omezeních tras a seznamu omezení poskytovaných architekturou najdete v části Omezení trasy.

Parametry trasy mohou mít také transformátory parametrů. Transformátory parametrů transformují hodnotu parametru při generování odkazů a odpovídajících akcí a stránek na adresy URL. Stejně jako omezení lze transformátory parametrů přidat do parametru trasy přidáním : názvu a transformátoru za název parametru trasy. Například šablona blog/{article:slugify} trasy určuje slugify transformátor. Další informace o transformátorech parametrů naleznete v části Transformátory parametrů .

Následující tabulka ukazuje ukázkové šablony tras a jejich chování:

Šablona trasy Příklad odpovídajícího identifikátoru URI Identifikátor URI požadavku...
hello /hello Odpovídá pouze jedné cestě /hello.
{Page=Home} / Odpovídá a nastaví Page na Home.
{Page=Home} /Contact Odpovídá a nastaví Page na Contact.
{controller}/{action}/{id?} /Products/List Mapuje se na Products kontroler a List akci.
{controller}/{action}/{id?} /Products/Details/123 Mapuje se Products na kontroler a Details akci s nastavenouid na hodnotu 123.
{controller=Home}/{action=Index}/{id?} / Mapuje se na kontroler a Index metoduHome. Vlastnost id je ignorována.
{controller=Home}/{action=Index}/{id?} /Products Mapuje se na kontroler a Index metoduProducts. Vlastnost id je ignorována.

Použití šablony je obecně nejjednodušším přístupem ke směrování. Omezení a výchozí hodnoty lze zadat také mimo šablonu trasy.

Komplexní segmenty

Komplexní segmenty se zpracovávají pomocí odpovídajících oddělovačů literálů zprava doleva způsobem, který není greedy . Jedná se například [Route("/a{b}c{d}")] o složitý segment. Složité segmenty fungují určitým způsobem, který je potřeba pochopit, aby je bylo možné úspěšně použít. Příklad v této části ukazuje, proč složité segmenty skutečně fungují dobře, když se text oddělovače nezobrazí uvnitř hodnot parametrů. Použití regulárního výrazu a ruční extrahování hodnot je potřeba pro složitější případy.

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Toto je souhrn kroků, které směrování provádí pomocí šablony /a{b}c{d} a cesty /abcdURL . Slouží | k vizualizaci fungování algoritmu:

  • První literál, zprava doleva, je c. Hledá se tedy /abcd zprava a najde /ab|c|d.
  • Vše napravo (d) se teď shoduje s parametrem {d}trasy .
  • Další literál, zprava doleva, je a. Takže /ab|c|d je prohledáno od místa, kde jsme skončili, pak a je nalezen /|a|b|c|d.
  • Hodnota vpravo (b) se teď shoduje s parametrem {b}trasy .
  • Neexistuje žádný zbývající text a žádná zbývající šablona trasy, takže se jedná o shodu.

Tady je příklad záporného případu, který používá stejnou šablonu /a{b}c{d} a cestu /aabcdURL . Slouží | k vizualizaci fungování algoritmu. Tento případ není shoda, která je vysvětlená stejným algoritmem:

  • První literál, zprava doleva, je c. Hledá se tedy /aabcd zprava a najde /aab|c|d.
  • Vše napravo (d) se teď shoduje s parametrem {d}trasy .
  • Další literál, zprava doleva, je a. Takže /aab|c|d je prohledáno od místa, kde jsme skončili, pak a je nalezen /a|a|b|c|d.
  • Hodnota vpravo (b) se teď shoduje s parametrem {b}trasy .
  • V tomto okamžiku existuje zbývající text a, ale algoritmus vyčeráhl šablonu trasy, aby parsovala, takže se nejedná o shodu.

Vzhledem k tomu, že odpovídající algoritmus není greedy:

  • Odpovídá nejmenšímu množství textu, který je možné v každém kroku.
  • Případ, kdy se hodnota oddělovače zobrazí uvnitř hodnot parametrů, způsobí, že se neshoduje.

Regulární výrazy poskytují mnohem větší kontrolu nad jejich odpovídajícím chováním.

Párování Greedy, označované také jako opožděné párování, odpovídá největšímu možnému řetězci. Greedy odpovídá nejmenšímu možnému řetězci.

Směrování se speciálními znaky

Směrování se speciálními znaky může vést k neočekávaným výsledkům. Představte si například kontroler s následující metodou akce:

[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null || todoItem.Name == null)
    {
        return NotFound();
    }

    return todoItem.Name;
}

Pokud string id obsahuje následující kódované hodnoty, mohou dojít k neočekávaným výsledkům:

ASCII Encoded
/ %2F
+

Parametry trasy nejsou vždy dekódovány adresou URL. Tento problém může být vyřešen v budoucnu. Další informace najdete v tomto problému na GitHubu.

Omezení trasy

Omezení směrování se provádějí, když došlo ke shodě s příchozí adresou URL a cesta url se tokenizuje do hodnot tras. Omezení tras obecně kontrolují hodnotu trasy přidruženou prostřednictvím šablony trasy a ověřte, jestli je hodnota přijatelná, nebo nepravdivá. Některá omezení trasy používají data mimo hodnotu trasy a zvažují, jestli je možné požadavek směrovat. Například HttpMethodRouteConstraint může přijmout nebo odmítnout požadavek na základě jeho příkazu HTTP. Omezení se používají v požadavcích směrování a generování propojení.

Upozorňující

Nepoužívejte omezení pro ověřování vstupu. Pokud se pro ověření vstupu používají omezení, výsledkem neplatného 404 vstupu je odpověď Nenalezena. Neplatný vstup by měl vytvořit chybný 400 požadavek s příslušnou chybovou zprávou. Omezení tras se používají k nejednoznačnosti podobných tras, nikoli k ověření vstupů pro konkrétní trasu.

Následující tabulka ukazuje ukázková omezení směrování a jejich očekávané chování:

omezení Příklad Příklady shod Notes
int {id:int} 123456789, -123456789 Odpovídá libovolnému celočíselnému číslu.
bool {active:bool} true, FALSE Shody true nebo false. Nerozlišují se malá a velká písmena
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Odpovídá platné DateTime hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
decimal {price:decimal} 49.99, -1,000.01 Odpovídá platné decimal hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
double {weight:double} 1.234, -1,001.01e8 Odpovídá platné double hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
float {weight:float} 1.234, -1,001.01e8 Odpovídá platné float hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Odpovídá platné Guid hodnotě.
long {ticks:long} 123456789, -123456789 Odpovídá platné long hodnotě.
minlength(value) {username:minlength(4)} Rick Řetězec musí mít alespoň 4 znaky.
maxlength(value) {filename:maxlength(8)} MyFile Řetězec nesmí být delší než 8 znaků.
length(length) {filename:length(12)} somefile.txt Řetězec musí mít přesně 12 znaků.
length(min,max) {filename:length(8,16)} somefile.txt Řetězec musí mít maximálně 8 znaků a nesmí být delší než 16 znaků.
min(value) {age:min(18)} 19 Celočíselná hodnota musí být alespoň 18.
max(value) {age:max(120)} 91 Celočíselná hodnota nesmí být větší než 120.
range(min,max) {age:range(18,120)} 91 Celočíselná hodnota musí být alespoň 18, ale nesmí být větší než 120.
alpha {name:alpha} Rick Řetězec musí obsahovat jeden nebo více abecedních znaků a-z a nerozlišuje velká a malá písmena.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 Řetězec musí odpovídat regulárnímu výrazu. Podívejte se na tipy k definování regulárního výrazu.
required {name:required} Rick Používá se k vynucení toho, aby během generování adresy URL byla přítomna hodnota, která není parametrem.

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Pro jeden parametr lze použít více omezení oddělených dvojtečkami. Například následující omezení omezuje parametr na celočíselnou hodnotu 1 nebo vyšší:

[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }

Upozorňující

Omezení směrování, která ověřují adresu URL a jsou převedeny na typ CLR, vždy používají neutrální jazykovou verzi. Například převod na typ int CLR nebo DateTime. Tato omezení předpokládají, že adresa URL není lokalizovatelná. Omezení tras poskytovaná architekturou nemění hodnoty uložené v hodnotách tras. Všechny hodnoty směrování parsované z adresy URL se ukládají jako řetězce. Omezení se například pokusí převést hodnotu trasy na hodnotu float, ale převedená hodnota se použije pouze k ověření, float že se dá převést na plovoucí hodnotu.

Regulární výrazy v omezeních

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Regulární výrazy lze zadat jako vložená omezení pomocí regex(...) omezení trasy. Metody v rodině MapControllerRoute také přijímají literál objektu omezení. Pokud se tento formulář použije, řetězcové hodnoty se interpretují jako regulární výrazy.

Následující kód používá omezení vložených regulárních výrazů:

app.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
    () => "Inline Regex Constraint Matched");

Následující kód používá literál objektu k určení omezení regulárního výrazu:

app.MapControllerRoute(
    name: "people",
    pattern: "people/{ssn}",
    constraints: new { ssn = "^\\d{3}-\\d{2}-\\d{4}$", },
    defaults: new { controller = "People", action = "List" });

Architektura ASP.NET Core přidává RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant do konstruktoru regulárních výrazů. Podívejte RegexOptions se na popis těchto členů.

Regulární výrazy používají oddělovače a tokeny podobné těm používaným směrováním a jazykem C#. Tokeny regulárních výrazů musí být uchycené. Chcete-li použít regulární výraz ^\d{3}-\d{2}-\d{4}$ v vložené omezení, použijte jednu z následujících možností:

  • Nahraďte \ znaky zadané v řetězci jako \\ znaky ve zdrojovém souboru jazyka C#, aby bylo možné řídicí znak řetězce utéct \ .
  • Doslovné řetězcové literály.

Chcete-li uvozovat znaky {oddělovače parametrů směrování , [}]zdvojnásobit znaky ve výrazu, {{například , }}, , . ]][[ Následující tabulka ukazuje regulární výraz a jeho řídicí verzi:

Regulární výraz Řídicí regulární výraz
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Regulární výrazy používané při směrování často začínají znakem ^ a odpovídají počáteční pozici řetězce. Výrazy často končí znakem $ a odpovídají konci řetězce. Znaky ^ a $ zajistěte, aby regulární výraz odpovídal celé hodnotě parametru trasy. ^ Bez znaků a $ znaků regulární výraz odpovídá jakémukoli podřetězci v řetězci, což je často nežádoucí. Následující tabulka obsahuje příklady a vysvětluje, proč se shodují nebo se neshodují:

Výraz String Párování Komentář
[a-z]{2} dobrý den Ano Shoda podřetěžce
[a-z]{2} 123abc456 Ano Shoda podřetěžce
[a-z]{2} mz Ano Odpovídá výrazu
[a-z]{2} MZ Ano Nerozlišuje se malá a velká písmena.
^[a-z]{2}$ dobrý den No Viz ^ a $ výše
^[a-z]{2}$ 123abc456 No Viz ^ a $ výše

Další informace o syntaxi regulárních výrazů naleznete v tématu Regulární výrazy rozhraní .NET Framework.

Chcete-li omezit parametr na známou sadu možných hodnot, použijte regulární výraz. Například {action:regex(^(list|get|create)$)} odpovídá pouze hodnotě action trasy do list, getnebo create. Pokud je řetězec předán do slovníku omezení, je řetězec ^(list|get|create)$ ekvivalentní. Omezení, která se předávají ve slovníku omezení, která neodpovídají některému ze známých omezení, se také považují za regulární výrazy. Omezení předaná v šabloně, která neodpovídají některému ze známých omezení, se považují za regulární výrazy.

Vlastní omezení trasy

Vlastní omezení trasy je možné vytvořit implementací IRouteConstraint rozhraní. Rozhraní IRouteConstraint obsahuje Match, který vrátí true , pokud je omezení splněno a false jinak.

Vlastní omezení trasy jsou zřídka nutná. Před implementací vlastního omezení trasy zvažte alternativy, jako je například vazba modelu.

Složka ASP.NET Core Constraints poskytuje dobré příklady vytváření omezení. Například GuidRouteConstraint.

Pokud chcete použít vlastní IRouteConstrainttyp omezení trasy, musí být zaregistrovaný v kontejneru služby v aplikaci ConstraintMap . A ConstraintMap je slovník, který mapuje klíče omezení směrování na IRouteConstraint implementace, které tato omezení ověřují. Aplikaci ConstraintMap je možné aktualizovat buď Program.cs v rámci AddRouting hovoru, nebo konfigurací RouteOptions přímo pomocí builder.Services.Configure<RouteOptions>. Příklad:

builder.Services.AddRouting(options =>
    options.ConstraintMap.Add("noZeroes", typeof(NoZeroesRouteConstraint)));

Předchozí omezení se použije v následujícím kódu:

[ApiController]
[Route("api/[controller]")]
public class NoZeroesController : ControllerBase
{
    [HttpGet("{id:noZeroes}")]
    public IActionResult Get(string id) =>
        Content(id);
}

Implementace NoZeroesRouteConstraint brání 0 použití v parametru trasy:

public class NoZeroesRouteConstraint : IRouteConstraint
{
    private static readonly Regex _regex = new(
        @"^[1-9]*$",
        RegexOptions.CultureInvariant | RegexOptions.IgnoreCase,
        TimeSpan.FromMilliseconds(100));

    public bool Match(
        HttpContext? httpContext, IRouter? route, string routeKey,
        RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (!values.TryGetValue(routeKey, out var routeValue))
        {
            return false;
        }

        var routeValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);

        if (routeValueString is null)
        {
            return false;
        }

        return _regex.IsMatch(routeValueString);
    }
}

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Předchozí kód:

  • Zabraňuje 0 v {id} segmentu trasy.
  • Ukazuje se, že poskytuje základní příklad implementace vlastního omezení. Neměla by se používat v produkční aplikaci.

Následující kód je lepším přístupem k tomu, aby se zabránilo zpracování obsahujícího id 0 :

[HttpGet("{id}")]
public IActionResult Get(string id)
{
    if (id.Contains('0'))
    {
        return StatusCode(StatusCodes.Status406NotAcceptable);
    }

    return Content(id);
}

Předchozí kód má oproti přístupu následující výhody NoZeroesRouteConstraint :

  • Nevyžaduje vlastní omezení.
  • Vrátí popisnější chybu, pokud parametr trasy obsahuje 0.

Transformátory parametrů

Transformátory parametrů:

Například vlastní slugify transformátor parametru ve vzoru blog\{article:slugify} trasy s Url.Action(new { article = "MyTestArticle" }) vygenerovanými blog\my-test-article.

Zvažte následující IOutboundParameterTransformer implementaci:

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string? TransformOutbound(object? value)
    {
        if (value is null)
        {
            return null;
        }

        return Regex.Replace(
            value.ToString()!,
                "([a-z])([A-Z])",
            "$1-$2",
            RegexOptions.CultureInvariant,
            TimeSpan.FromMilliseconds(100))
            .ToLowerInvariant();
    }
}

Chcete-li použít transformátor parametrů ve vzoru trasy, nakonfigurujte ho pomocí :ConstraintMap Program.cs

builder.Services.AddRouting(options =>
    options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer));

Architektura ASP.NET Core používá transformátory parametrů k transformaci identifikátoru URI, kde se koncový bod překládá. Například transformátory parametrů transformují směrovací hodnoty použité ke shodě area, controller, actiona page:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");

U předchozí šablony trasy se akce SubscriptionManagementController.GetAll shoduje s identifikátorem URI /subscription-management/get-all. Transformátor parametrů nemění směrovací hodnoty použité k vygenerování propojení. Například Url.Action("GetAll", "SubscriptionManagement") výstupy /subscription-management/get-all.

ASP.NET Core poskytuje konvence rozhraní API pro použití transformátorů parametrů se generovanými trasami:

Referenční informace ke generování adres URL

Tato část obsahuje odkaz na algoritmus implementovaný generováním adres URL. V praxi používají nejsložitější příklady generování adres URL kontrolery nebo Razor stránky. Další informace najdete v tématu Směrování v kontrolerů .

Proces generování adresy URL začíná voláním LinkGenerator.GetPathByAddress nebo podobnou metodou. Metoda je poskytována s adresou, sadou hodnot tras a volitelně informace o aktuálním požadavku z HttpContext.

Prvním krokem je použití adresy k vyřešení sady kandidátských koncových bodů pomocí typu IEndpointAddressScheme<TAddress> adresy.

Jakmile sada kandidátů najde schéma adres, koncové body se objednávají a zpracovávají iterativním způsobem, dokud operace generování adresy URL nebude úspěšná. Generování adresy URL nekontroluje nejednoznačnosti, první vrácený výsledek je konečný výsledek.

Řešení potíží s generováním adres URL pomocí protokolování

Prvním krokem při generování adresy URL při řešení potíží je nastavení úrovně Microsoft.AspNetCore.Routing protokolování na TRACE. LinkGenerator zaznamenává mnoho podrobností o jeho zpracování, které může být užitečné při řešení problémů.

Podrobnosti o generování adres URL najdete v referenčních informacích ke generování adres URL.

Adresy

Adresy jsou konceptem generování adres URL, které slouží k vytvoření vazby volání do generátoru odkazů na sadu kandidátských koncových bodů.

Adresy jsou rozšiřitelný koncept, který ve výchozím nastavení obsahuje dvě implementace:

  • Jako adresu použijte název koncového bodu (string):
    • Poskytuje podobné funkce jako název trasy MVC.
    • IEndpointNameMetadata Používá typ metadat.
    • Vyřeší zadaný řetězec s metadaty všech registrovaných koncových bodů.
    • Vyvolá výjimku při spuštění, pokud více koncových bodů používá stejný název.
    • Doporučuje se pro obecné použití mimo kontrolery a Razor stránky.
  • Použití hodnot směrování (RouteValuesAddress) jako adresy:
    • Poskytuje podobné funkce jako kontrolery a Razor starší generace adres URL stránky.
    • Velmi složité rozšířit a ladit.
    • Poskytuje implementaci, kterou IUrlHelperpoužívají pomocné rutiny značek, pomocné rutiny HTML, výsledky akcí atd.

Role schématu adres spočívá v přidružení mezi adresou a odpovídajícími koncovými body podle libovolných kritérií:

  • Schéma názvu koncového bodu provádí základní vyhledávání slovníku.
  • Schéma hodnot tras má komplexní nejlepší podmnožinu algoritmu sady.

Okolní hodnoty a explicitní hodnoty

Z aktuálního požadavku směrování přistupuje ke směrovacím hodnotám aktuálního požadavku HttpContext.Request.RouteValues. Hodnoty přidružené k aktuálnímu požadavku se označují jako okolní hodnoty. Pro účely srozumitelnosti se dokumentace týká směrovacích hodnot předávaných metodám jako explicitních hodnot.

Následující příklad ukazuje okolní hodnoty a explicitní hodnoty. Poskytuje okolní hodnoty z aktuálního požadavku a explicitních hodnot:

public class WidgetController : ControllerBase
{
    private readonly LinkGenerator _linkGenerator;

    public WidgetController(LinkGenerator linkGenerator) =>
        _linkGenerator = linkGenerator;

    public IActionResult Index()
    {
        var indexPath = _linkGenerator.GetPathByAction(
            HttpContext, values: new { id = 17 })!;

        return Content(indexPath);
    }

    // ...

Předchozí kód:

Následující kód poskytuje pouze explicitní hodnoty a žádné okolní hodnoty:

var subscribePath = _linkGenerator.GetPathByAction(
    "Subscribe", "Home", new { id = 17 })!;

Předchozí metoda vrátí /Home/Subscribe/17

Následující kód ve návratu WidgetController /Widget/Subscribe/17:

var subscribePath = _linkGenerator.GetPathByAction(
    HttpContext, "Subscribe", null, new { id = 17 });

Následující kód poskytuje kontroler z okolních hodnot v aktuálním požadavku a explicitní hodnoty:

public class GadgetController : ControllerBase
{
    public IActionResult Index() =>
        Content(Url.Action("Edit", new { id = 17 })!);
}

V předchozím kódu:

  • /Gadget/Edit/17 je vrácena.
  • UrlIUrlHelperzíská .
  • Action vygeneruje adresu URL s absolutní cestou pro metodu akce. Adresa URL obsahuje zadaný action název a route hodnoty.

Následující kód poskytuje okolní hodnoty z aktuálního požadavku a explicitní hodnoty:

public class IndexModel : PageModel
{
    public void OnGet()
    {
        var editUrl = Url.Page("./Edit", new { id = 17 });

        // ...
    }
}

Předchozí kód se nastaví url , když /Edit/17 Edit Razor Page obsahuje následující direktivu stránky:

@page "{id:int}"

Pokud stránka Upravit neobsahuje "{id:int}" šablonu trasy, url je /Edit?id=17.

Chování MVC IUrlHelper přidává kromě zde popsaných pravidel vrstvu složitosti:

  • IUrlHelper vždy poskytuje směrovací hodnoty z aktuálního požadavku jako okolní hodnoty.
  • IUrlHelper.Action vždy zkopíruje aktuální action hodnoty a controller směrovací hodnoty jako explicitní hodnoty, pokud je nepřepíše vývojář.
  • IUrlHelper.Page vždy zkopíruje aktuální page hodnotu trasy jako explicitní hodnotu, pokud ji nepřepíšete.
  • IUrlHelper.Page vždy přepíše aktuální handler hodnotu null trasy jako explicitní hodnoty, pokud ji nepřepíšete.

Uživatelé jsou často překvapeni chováním okolních hodnot, protože MVC zdánlivě nedodržuje vlastní pravidla. Z historických důvodů a z důvodu kompatibility mají určité hodnoty tras, jako actionje , controller, pagea handler mají vlastní zvláštní chování.

Ekvivalentní funkce poskytované LinkGenerator.GetPathByAction a LinkGenerator.GetPathByPage duplikuje tyto anomálie z důvodu kompatibility IUrlHelper .

Proces generování adres URL

Jakmile se najde sada kandidátských koncových bodů, algoritmus generování adres URL:

  • Zpracovává koncové body iterativním způsobem.
  • Vrátí první úspěšný výsledek.

Prvním krokem v tomto procesu je zneplatnění hodnoty trasy. Zneplatnění hodnoty trasy je proces, kterým směrování rozhodne, které hodnoty trasy z okolních hodnot se mají použít a které by se měly ignorovat. Každá okolní hodnota se považuje a buď v kombinaci s explicitními hodnotami, nebo je ignorována.

Nejlepším způsobem, jak přemýšlet o roli okolních hodnot, je, že se snaží uložit vývojáři aplikací psaní, v některých běžných případech. Scénáře, ve kterých jsou okolní hodnoty užitečné, se tradičně vztahují k MVC:

  • Při propojení s jinou akcí ve stejném řadiči není nutné zadat název kontroleru.
  • Při propojení s jiným řadičem ve stejné oblasti není nutné zadat název oblasti.
  • Při propojení se stejnou metodou akce není nutné zadávat hodnoty tras.
  • Při propojení s jinou částí aplikace nechcete přenášet hodnoty tras, které nemají v této části aplikace žádný význam.

Volání nebo LinkGenerator IUrlHelper vrácení jsou obvykle způsobená tím, že null nerozumí neplatné hodnotě trasy. Při řešení potíží s neplatnou hodnotou trasy můžete explicitně zadat více hodnot tras, abyste zjistili, jestli se tím problém vyřeší.

Neplatná hodnota trasy funguje na předpokladu, že schéma adresy URL aplikace je hierarchické s hierarchií vytvořenou zleva doprava. Představte si šablonu {controller}/{action}/{id?} směrování základního kontroleru, abyste získali intuitivní představu o tom, jak to funguje v praxi. Změna hodnoty zneplatní všechny směrovací hodnoty, které se zobrazují vpravo. To odráží předpoklad o hierarchii. Pokud má aplikace okolní hodnotu a idoperace určuje jinou hodnotu pro controller:

  • id nebude znovu použito, protože {controller} je nalevo od {id?}.

Několik příkladů demonstrujících tento princip:

  • Pokud explicitní hodnoty obsahují hodnotu pro id, okolní hodnota je id ignorována. Okolní hodnoty a controller action lze je použít.
  • Pokud explicitní hodnoty obsahují hodnotu pro action, je ignorována jakákoli okolní hodnota pro action . Lze použít okolní hodnoty controller . Pokud se explicitní hodnota pro jinou než okolní hodnota action actionpro , id hodnota se nepoužije. Pokud je explicitní hodnota action stejná jako okolní hodnota pro action, id lze použít hodnotu.
  • Pokud explicitní hodnoty obsahují hodnotu pro controller, je ignorována jakákoli okolní hodnota pro controller . Pokud se explicitní hodnota pro jinou než okolní hodnota controller controllerpro , action hodnoty a id hodnoty nebudou použity. Pokud je explicitní hodnota controller stejná jako okolní hodnota pro controller, action lze použít hodnoty a id hodnoty.

Tento proces je ještě složitější díky existenci tras atributů a vyhrazených konvenčních tras. Běžné trasy kontroleru, jako {controller}/{action}/{id?} je například určení hierarchie pomocí parametrů trasy. Pro vyhrazené konvenční trasy a trasy atributů pro kontrolery a Razor stránky:

  • Existuje hierarchie hodnot tras.
  • Nezobrazují se v šabloně.

V těchto případech generování adresy URL definuje koncept požadovaných hodnot . Koncové body vytvořené kontrolery a Razor stránkami mají zadané požadované hodnoty, které umožňují zneplatnění hodnoty trasy.

Podrobný algoritmus zneplatnění hodnoty trasy:

  • Požadované názvy hodnot se kombinují s parametry trasy a pak se zpracovávají zleva doprava.
  • Pro každý parametr se porovná okolní hodnota a explicitní hodnota:
    • Pokud je okolní hodnota a explicitní hodnota stejné, proces pokračuje.
    • Pokud je okolní hodnota přítomná a explicitní hodnota není, použije se při generování adresy URL okolní hodnota.
    • Pokud okolní hodnota není přítomná a explicitní hodnota je, zamítněte okolní hodnotu a všechny následné okolní hodnoty.
    • Pokud se nachází okolí a explicitní hodnota a obě hodnoty jsou odlišné, zamítněte okolní hodnotu a všechny následné okolní hodnoty.

V tuto chvíli je operace generování adres URL připravená k vyhodnocení omezení trasy. Sada přijatých hodnot se zkombinuje s výchozími hodnotami parametrů, které jsou k dispozici pro omezení. Pokud všechna omezení projdou, operace bude pokračovat.

Dále je možné použít akceptované hodnoty k rozšíření šablony trasy. Šablona trasy se zpracuje:

  • Zleva doprava.
  • Každý parametr má jeho přijatou hodnotu nahrazenou.
  • S následujícími zvláštními případy:
    • Pokud přijaté hodnoty chybí hodnota a parametr má výchozí hodnotu, použije se výchozí hodnota.
    • Pokud přijaté hodnoty chybí a parametr je nepovinný, zpracování pokračuje.
    • Pokud některý parametr trasy napravo od chybějícího volitelného parametru má hodnotu, operace selže.
    • Pokud je to možné, sbalí se souvislé parametry s výchozími hodnotami a volitelné parametry.

Hodnoty explicitně za předpokladu, že neodpovídají segmentu trasy, se přidají do řetězce dotazu. Následující tabulka ukazuje výsledek při použití šablony {controller}/{action}/{id?}trasy .

Okolní hodnoty Explicitní hodnoty Výsledek
controller = "Home" action = "O aplikaci" /Home/About
controller = "Home" controller = "Order", action = "About" /Order/About
controller = "Home", color = "Red" action = "O aplikaci" /Home/About
controller = "Home" action = "O produktu", barva = "Červená" /Home/About?color=Red

Problémy s neplatnou hodnotou trasy

Následující kód ukazuje příklad schématu generování adres URL, které není podporováno směrováním:

app.MapControllerRoute(
    "default",
    "{culture}/{controller=Home}/{action=Index}/{id?}");

app.MapControllerRoute(
    "blog",
    "{culture}/{**slug}",
    new { controller = "Blog", action = "ReadPost" });

V předchozím kódu culture se parametr trasy používá pro lokalizaci. Touha má parametr culture vždy přijmout jako okolní hodnotu. Parametr culture však není přijat jako okolní hodnota kvůli způsobu, jakým požadované hodnoty fungují:

  • "default" V šabloně culture trasy je parametr trasy vlevo od controller, takže změny controller nebudou cultureneplatné .
  • "blog" V šabloně culture trasy se parametr trasy považuje za napravo od controllerparametru trasy, který se zobrazí v požadovaných hodnotách.

Parsování cest URL pomocí LinkParser

Třída LinkParser přidává podporu analýzy cesty URL do sady hodnot tras. Metoda ParsePathByEndpointName přebírá název koncového bodu a cestu URL a vrátí sadu hodnot tras extrahovaných z cesty URL.

V následujícím příkladu kontroler akce GetProduct používá šablonu api/Products/{id} trasy a má Name :GetProduct

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}", Name = nameof(GetProduct))]
    public IActionResult GetProduct(string id)
    {
        // ...

Ve stejné třídě AddRelatedProduct kontroleru akce očekává cestu URL, pathToRelatedProductkterou lze poskytnout jako parametr řetězce dotazu:

[HttpPost("{id}/Related")]
public IActionResult AddRelatedProduct(
    string id, string pathToRelatedProduct, [FromServices] LinkParser linkParser)
{
    var routeValues = linkParser.ParsePathByEndpointName(
        nameof(GetProduct), pathToRelatedProduct);
    var relatedProductId = routeValues?["id"];

    // ...

V předchozím příkladu AddRelatedProduct akce extrahuje id hodnotu trasy z cesty URL. Například s cestou /api/Products/1URL , relatedProductId hodnota je nastavena na 1. Tento přístup umožňuje klientům rozhraní API používat cesty url při odkazování na prostředky, aniž by museli vědět, jak je taková adresa URL strukturovaná.

Konfigurace metadat koncového bodu

Následující odkazy obsahují informace o konfiguraci metadat koncového bodu:

Porovnávání hostitelů v trasách pomocí RequireHost

RequireHost použije omezení na trasu, která vyžaduje zadaného hostitele. Parametrem RequireHost [Host] může být:

  • Hostitel: www.domain.com, odpovídá www.domain.com libovolnému portu.
  • Hostitel se zástupným znakem: *.domain.com, shody www.domain.com, subdomain.domain.comnebo www.subdomain.domain.com na libovolném portu.
  • Port: *:5000Odpovídá portu 5000 s libovolným hostitelem.
  • Hostitel a port: www.domain.com:5000 nebo *.domain.com:5000odpovídá hostiteli a portu.

Lze zadat více parametrů pomocí RequireHost nebo [Host]. Omezení odpovídá hostitelům platným pro některý z parametrů. Například [Host("domain.com", "*.domain.com")] shody domain.com, www.domain.coma subdomain.domain.com.

Následující kód používá RequireHost k vyžadování zadaného hostitele na trase:

app.MapGet("/", () => "Contoso").RequireHost("contoso.com");
app.MapGet("/", () => "AdventureWorks").RequireHost("adventure-works.com");

app.MapHealthChecks("/healthz").RequireHost("*:8080");

Následující kód používá [Host] atribut na kontroleru k vyžadování některého ze zadaných hostitelů:

[Host("contoso.com", "adventure-works.com")]
public class HostsController : Controller
{
    public IActionResult Index() =>
        View();

    [Host("example.com")]
    public IActionResult Example() =>
        View();
}

[Host] Když se atribut použije na metodu kontroleru i akce:

  • Použije se atribut akce.
  • Atribut kontroleru je ignorován.

Doprovodné materiály k výkonu pro směrování

Pokud má aplikace problémy s výkonem, směrování se často považuje za problém. Důvodem je podezření, že architektury, jako jsou kontrolery a Razor stránky, hlásí dobu strávenou uvnitř rozhraní v protokolovacích zprávách. Pokud je mezi časem hlášeným kontrolery a celkovým časem požadavku významný rozdíl:

  • Vývojáři eliminují kód aplikace jako zdroj problému.
  • Běžně se předpokládá, že příčinou je směrování.

Směrování je testované výkonem pomocí tisíců koncových bodů. Je nepravděpodobné, že by typická aplikace narazila na problém s výkonem jenom tím, že je příliš velká. Nejčastější hlavní příčinou pomalého směrování je obvykle špatně se chovající vlastní middleware.

Následující ukázka kódu ukazuje základní techniku zúžení zdroje zpoždění:

var logger = app.Services.GetRequiredService<ILogger<Program>>();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.UseRouting();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.UseAuthorization();

app.Use(async (context, next) =>
{
    var stopwatch = Stopwatch.StartNew();
    await next(context);
    stopwatch.Stop();

    logger.LogInformation("Time 3: {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
});

app.MapGet("/", () => "Timing Test.");

Směrování podle času:

  • Prokládání každého middlewaru pomocí kopie middlewaru časování zobrazeného v předchozím kódu.
  • Přidejte jedinečný identifikátor pro korelaci dat časování s kódem.

Jedná se o základní způsob, jak zúžit zpoždění, pokud je významné, například více než 10ms. Odečítá Time 2 se od Time 1 sestav čas strávený v middlewaru UseRouting .

Následující kód používá kompaktnější přístup k předchozímu kódu časování:

public sealed class AutoStopwatch : IDisposable
{
    private readonly ILogger _logger;
    private readonly string _message;
    private readonly Stopwatch _stopwatch;
    private bool _disposed;

    public AutoStopwatch(ILogger logger, string message) =>
        (_logger, _message, _stopwatch) = (logger, message, Stopwatch.StartNew());

    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        _logger.LogInformation("{Message}: {ElapsedMilliseconds}ms",
            _message, _stopwatch.ElapsedMilliseconds);

        _disposed = true;
    }
}
var logger = app.Services.GetRequiredService<ILogger<Program>>();
var timerCount = 0;

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.UseRouting();

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.UseAuthorization();

app.Use(async (context, next) =>
{
    using (new AutoStopwatch(logger, $"Time {++timerCount}"))
    {
        await next(context);
    }
});

app.MapGet("/", () => "Timing Test.");

Potenciálně nákladné funkce směrování

Následující seznam obsahuje přehled o funkcích směrování, které jsou relativně drahé v porovnání se základními šablonami tras:

  • Regulární výrazy: Je možné psát regulární výrazy, které jsou složité nebo mají dlouhou dobu trvání s malým množstvím vstupu.
  • Komplexní segmenty ({x}-{y}-{z}):
    • Jsou výrazně dražší než analýza běžného segmentu cesty URL.
    • Výsledkem je přidělení mnoha dalších podřetěžců.
  • Synchronní přístup k datům: Mnoho složitých aplikací má v rámci směrování přístup k databázi. Použijte body rozšiřitelnosti, například MatcherPolicy a EndpointSelectorContext, které jsou asynchronní.

Pokyny pro velké směrovací tabulky

Ve výchozím nastavení ASP.NET Core používá algoritmus směrování, který obchoduje s pamětí za čas procesoru. To má pěkný účinek, že čas porovnávání tras závisí pouze na délce cesty, která se má shodovat, a ne na počtu tras. Tento přístup ale může být v některých případech problematický, když má aplikace velký počet tras (v tisících) a v trasách existuje velké množství předpon proměnných. Například pokud trasy mají parametry v počátečních segmentech trasy, například {parameter}/some/literal.

Je nepravděpodobné, že by aplikace narazila na situaci, kdy se jedná o problém, pokud:

  • V aplikaci existuje velký počet tras, které tento model používají.
  • V aplikaci je velký počet tras.

Jak zjistit, jestli aplikace narazí na problém s velkou směrovací tabulkou

  • Existují dva příznaky, které je třeba hledat:
    • Aplikace se pomalu spustí při prvním požadavku.
      • Mějte na paměti, že je to povinné, ale nestačí. Existuje mnoho dalších problémů, které se nesměrují, než může způsobit pomalé spuštění aplikace. Zkontrolujte níže uvedený stav, abyste přesně zjistili, jestli aplikace v této situaci neběží.
    • Aplikace během spouštění spotřebovává velké množství paměti a výpis paměti zobrazuje velký počet Microsoft.AspNetCore.Routing.Matching.DfaNode instancí.

Jak tento problém vyřešit

U tras lze použít několik technik a optimalizací, které tento scénář z velké části zlepší:

  • Pokud je to možné, použijte omezení trasy na parametry, například {parameter:int}{parameter:guid}, {parameter:regex(\\d+)}, atd.
    • To umožňuje algoritmus směrování interně optimalizovat struktury používané pro porovnávání a výrazně snížit využitou paměť.
    • Ve většině případů to stačí, abyste se vrátili k přijatelnému chování.
  • Změňte trasy tak, aby se parametry přesunuly do pozdějších segmentů v šabloně.
    • Tím se sníží počet možných cest tak, aby odpovídal koncovému bodu dané cestě.
  • Použijte dynamickou trasu a dynamicky proveďte mapování na kontroler nebo stránku.
    • Toho lze dosáhnout pomocí MapDynamicControllerRoute a MapDynamicPageRoute.

Pokyny pro autory knihoven

Tato část obsahuje pokyny pro autory knihoven, kteří vycházejí ze směrování. Cílem těchto podrobností je zajistit, aby vývojáři aplikací měli dobré zkušenosti s používáním knihoven a architektur, které rozšiřují směrování.

Definování koncových bodů

Pokud chcete vytvořit architekturu, která používá směrování pro porovnávání adres URL, začněte definováním uživatelského prostředí, které je postavené na UseEndpoints.

DO staví na vrcholu IEndpointRouteBuilder. To umožňuje uživatelům vytvářet architekturu s jinými funkcemi ASP.NET Core bez nejasností. Každá šablona ASP.NET Core zahrnuje směrování. Předpokládejme, že směrování je pro uživatele k dispozici a známé.

// Your framework
app.MapMyFramework(...);

app.MapHealthChecks("/healthz");

DO vrátit zapečetěný beton typ z volání MapMyFramework(...) , které implementuje IEndpointConventionBuilder. Většina metod architektury Map... tento vzor dodržuje. Rozhraní IEndpointConventionBuilder :

  • Umožňuje složená metadata.
  • Cílí na různé rozšiřující metody.

Deklarování vlastního typu umožňuje do tvůrce přidat vlastní funkce specifické pro architekturu. Je v pořádku zabalit tvůrce deklarovaného architekturou a předat do něj volání.

// Your framework
app.MapMyFramework(...)
    .RequireAuthorization()
    .WithMyFrameworkFeature(awesome: true);

app.MapHealthChecks("/healthz");

ZVAŽTE psaní vlastního EndpointDataSource. EndpointDataSource je primitivní úroveň nízké úrovně pro deklarování a aktualizaci kolekce koncových bodů. EndpointDataSource je výkonné rozhraní API používané kontrolery a Razor stránkami.

Testy směrování mají základní příklad neaktualizuje zdroje dat.

VE výchozím nastavení se nepokoušejte EndpointDataSource o registraci. Vyžadovat, aby uživatelé zaregistrovali vaši architekturu v UseEndpoints. Filozofie směrování spočívá v tom, že ve výchozím nastavení se nic nezahrnuje a je to UseEndpoints místo pro registraci koncových bodů.

Vytvoření middlewaru integrovaného se směrováním

ZVAŽTE definování typů metadat jako rozhraní.

Umožňuje použít typy metadat jako atribut tříd a metod.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => true;
}

Architektury, jako jsou kontrolery a Razor stránky, podporují použití atributů metadat na typy a metody. Pokud deklarujete typy metadat:

  • Zpřístupní je jako atributy.
  • Většina uživatelů má zkušenosti s používáním atributů.

Deklarace typu metadat jako rozhraní přidává další vrstvu flexibility:

  • Rozhraní jsou kompozibilní.
  • Vývojáři můžou deklarovat své vlastní typy, které kombinují více zásad.

Umožňuje přepsat metadata, jak je znázorněno v následujícím příkladu:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SuppressCoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => false;
}

[CoolMetadata]
public class MyController : Controller
{
    public void MyCool() { }

    [SuppressCoolMetadata]
    public void Uncool() { }
}

Nejlepší způsob, jak postupovat podle těchto pokynů, je vyhnout se definování metadat značek:

  • Nehledáte jenom přítomnost typu metadat.
  • Definujte vlastnost metadat a zkontrolujte vlastnost.

Kolekce metadat je seřazená a podporuje přepsání podle priority. V případě kontrolerů jsou metadata pro metodu akce nejvýraznější.

Do make middleware užitečný s a bez směrování:

app.UseAuthorization(new AuthorizationPolicy() { ... });

// Your framework
app.MapMyFramework(...).RequireAuthorization();

Jako příklad tohoto návodu UseAuthorization zvažte middleware. Middleware pro autorizaci umožňuje předávat záložní zásady. Záložní zásada, pokud je zadaná, platí pro obě:

  • Koncové body bez zadané zásady
  • Požadavky, které neodpovídají koncovému bodu.

Díky tomu je middleware autorizace užitečný mimo kontext směrování. Autorizační middleware lze použít pro tradiční programování middlewaru.

Ladění diagnostiky

Podrobný výstup diagnostiky směrování nastavíte Logging:LogLevel:Microsoft na Debughodnotu . Ve vývojovém prostředí nastavte úroveň protokolu v appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

Další materiály

Směrování zodpovídá za porovnávání příchozích požadavků HTTP a odesílání těchto požadavků do spustitelných koncových bodů aplikace. Koncové body jsou jednotky spustitelného kódu zpracování požadavků. Koncové body se definují v aplikaci a konfigurují se při spuštění aplikace. Proces porovnávání koncových bodů může extrahovat hodnoty z adresy URL požadavku a poskytnout tyto hodnoty pro zpracování požadavků. Pomocí informací o koncovém bodu z aplikace je směrování také schopné generovat adresy URL, které se mapují na koncové body.

Aplikace můžou nakonfigurovat směrování pomocí:

Tento dokument popisuje podrobnosti nízké úrovně směrování ASP.NET Core. Informace o konfiguraci směrování:

Systém směrování koncových bodů popsaný v tomto dokumentu platí pro ASP.NET Core 3.0 a novější. Informace o předchozím systému směrování založeném na IRouternástroji , vyberte verzi ASP.NET Core 2.1 pomocí jednoho z následujících přístupů:

Zobrazení nebo stažení ukázkového kódu (postup stažení)

Ukázky stahování pro tento dokument jsou povoleny konkrétní Startup třídou. Pokud chcete spustit konkrétní ukázku, upravte Program.cs volání požadované Startup třídy.

Základy směrování

Všechny šablony ASP.NET Core zahrnují směrování do vygenerovaného kódu. Směrování je registrováno v kanálu middlewaru v Startup.Configure.

Následující kód ukazuje základní příklad směrování:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

Směrování používá dvojici middlewaru, zaregistrovaného uživatelem UseRouting a UseEndpoints:

  • UseRouting přidá trasu odpovídající kanálu middlewaru. Tento middleware se podívá na sadu koncových bodů definovaných v aplikaci a vybere nejlepší shodu na základě požadavku.
  • UseEndpoints přidá spuštění koncového bodu do kanálu middlewaru. Spustí delegáta přidruženého k vybranému koncovému bodu.

Předchozí příklad obsahuje jednu trasu ke koncovému bodu kódu pomocí metody MapGet :

  • Při odeslání požadavku HTTP GET na kořenovou adresu URL /:
    • Spustí se delegát požadavku.
    • Hello World! je zapsán do odpovědi HTTP. Ve výchozím nastavení je https://localhost:5001/kořenová adresa URL / .
  • Pokud metoda požadavku není GET nebo kořenová adresa URL není /, žádná trasa neodpovídá a vrátí se http 404.

Koncový bod

Metoda MapGet se používá k definování koncového bodu. Koncový bod je něco, co může být:

  • Vybráno tak, že se shoduje s adresou URL a metodou HTTP.
  • Spustí se spuštěním delegáta.

Koncové body, které se dají spárovat a spouštět aplikací, jsou nakonfigurované v UseEndpoints. Například MapGet, MapPosta podobné metody připojit žádosti delegáty do systému směrování. Další metody lze použít k připojení funkcí architektury ASP.NET Core ke směrovacímu systému:

Následující příklad ukazuje směrování s sofistikovanější šablonou trasy:

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/hello/{name:alpha}", async context =>
    {
        var name = context.Request.RouteValues["name"];
        await context.Response.WriteAsync($"Hello {name}!");
    });
});

Řetězec /hello/{name:alpha} je šablona trasy. Slouží ke konfiguraci shody koncového bodu. V tomto případě šablona odpovídá:

  • Adresa URL, jako je /hello/Ryan
  • Všechny cesty URL, které začínají /hello/ posloupností abecedních znaků. :alpha použije omezení trasy, které odpovídá pouze abecedním znakům. Omezení tras jsou vysvětlena dále v tomto dokumentu.

Druhý segment cesty URL: {name:alpha}

Systém směrování koncových bodů popsaný v tomto dokumentu je od ASP.NET Core 3.0 nový. Všechny verze ASP.NET Core ale podporují stejnou sadu funkcí šablon tras a omezení tras.

Následující příklad ukazuje směrování s kontrolami stavu a autorizací:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    // Matches request to an endpoint.
    app.UseRouting();

    // Endpoint aware middleware. 
    // Middleware can use metadata from the matched endpoint.
    app.UseAuthentication();
    app.UseAuthorization();

    // Execute the matched endpoint.
    app.UseEndpoints(endpoints =>
    {
        // Configure the Health Check endpoint and require an authorized user.
        endpoints.MapHealthChecks("/healthz").RequireAuthorization();

        // Configure another endpoint, no authorization requirements.
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

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.

Předchozí příklad ukazuje, jak:

  • S směrováním je možné použít autorizační middleware.
  • Koncové body je možné použít ke konfiguraci chování autorizace.

Volání MapHealthChecks přidá koncový bod kontroly stavu. Při zřetězování RequireAuthorization k tomuto volání se ke koncovému bodu připojí zásady autorizace.

Volání UseAuthentication a UseAuthorization přidání middlewaru pro ověřování a autorizaci Tyto middleware jsou umístěny mezi UseRouting a UseEndpoints tak, aby mohly:

  • Podívejte se, který koncový bod vybral UseRouting.
  • Před odesláním do koncového bodu použijte zásadu UseEndpoints autorizace.

Metadata koncového bodu

V předchozím příkladu existují dva koncové body, ale k autorizační zásadě jsou připojené jenom koncové body kontroly stavu. Pokud požadavek odpovídá koncovému bodu kontroly stavu, /healthzprovede se kontrola autorizace. To ukazuje, že koncové body můžou mít připojená další data. Tato další data se nazývají metadata koncového bodu:

  • Metadata je možné zpracovat pomocí middlewaru pracujícího se směrováním.
  • Metadata můžou být libovolného typu .NET.

Koncepty směrování

Systém směrování vychází z kanálu middlewaru přidáním výkonného konceptu koncového bodu . Koncové body představují jednotky funkcí aplikace, které se vzájemně liší z hlediska směrování, autorizace a libovolného počtu systémů ASP.NET Core.

Definice koncového bodu ASP.NET Core

Koncový bod ASP.NET Core je:

Následující kód ukazuje, jak načíst a zkontrolovat koncový bod odpovídající aktuálnímu požadavku:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.Use(next => context =>
    {
        var endpoint = context.GetEndpoint();
        if (endpoint is null)
        {
            return Task.CompletedTask;
        }
        
        Console.WriteLine($"Endpoint: {endpoint.DisplayName}");

        if (endpoint is RouteEndpoint routeEndpoint)
        {
            Console.WriteLine("Endpoint has route pattern: " +
                routeEndpoint.RoutePattern.RawText);
        }

        foreach (var metadata in endpoint.Metadata)
        {
            Console.WriteLine($"Endpoint has metadata: {metadata}");
        }

        return Task.CompletedTask;
    });

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

Koncový bod, pokud je vybraný, lze načíst z objektu HttpContext. Jeho vlastnosti je možné zkontrolovat. Objekty koncového bodu jsou neměnné a po vytvoření není možné je změnit. Nejběžnějším typem RouteEndpointkoncového bodu je . RouteEndpoint obsahuje informace, které mu umožní vybrat směrovací systém.

V předchozím kódu aplikace. Slouží ke konfiguraci in-line middlewaru.

Následující kód ukazuje, že v závislosti na tom, kde app.Use se v kanálu volá, nemusí existovat koncový bod:

// Location 1: before routing runs, endpoint is always null here
app.Use(next => context =>
{
    Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    return next(context);
});

app.UseRouting();

// Location 2: after routing runs, endpoint will be non-null if routing found a match
app.Use(next => context =>
{
    Console.WriteLine($"2. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    return next(context);
});

app.UseEndpoints(endpoints =>
{
    // Location 3: runs when this endpoint matches
    endpoints.MapGet("/", context =>
    {
        Console.WriteLine(
            $"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
        return Task.CompletedTask;
    }).WithDisplayName("Hello");
});

// Location 4: runs after UseEndpoints - will only run if there was no match
app.Use(next => context =>
{
    Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");
    return next(context);
});

Tato předchozí ukázka přidá Console.WriteLine příkazy, které zobrazují, jestli byl vybrán koncový bod nebo ne. Pro přehlednost ukázka přiřadí zadaný koncový bod zobrazovaný název / .

Spuštění tohoto kódu s adresou URL / zobrazení:

1. Endpoint: (null)
2. Endpoint: Hello
3. Endpoint: Hello

Spuštění tohoto kódu s jakoukoli jinou adresou URL zobrazí:

1. Endpoint: (null)
2. Endpoint: (null)
4. Endpoint: (null)

Tento výstup ukazuje, že:

  • Koncový bod je před zavolání vždy null UseRouting .
  • Pokud se najde shoda, koncový bod je mezi UseRouting a UseEndpoints.
  • Middleware UseEndpoints je terminál , když se najde shoda. Middleware terminálu je definován dále v tomto dokumentu.
  • Middleware po spuštění pouze v UseEndpoints případě, že nebyla nalezena žádná shoda.

Middleware UseRouting používá metodu SetEndpoint k připojení koncového bodu k aktuálnímu kontextu. Middleware je možné nahradit UseRouting vlastní logikou a přesto získat výhody používání koncových bodů. Koncové body jsou primitivní nízké úrovně, jako je middleware, a nejsou svázané s implementací směrování. Většina aplikací nemusí nahradit UseRouting vlastní logikou.

Middleware UseEndpoints je navržený tak, aby se používal společně s middlewarem UseRouting . Základní logika pro spuštění koncového bodu není složitá. Slouží GetEndpoint k načtení koncového bodu a následnému vyvolání jeho RequestDelegate vlastnosti.

Následující kód ukazuje, jak může middleware ovlivnit nebo reagovat na směrování:

public class IntegratedMiddlewareStartup
{ 
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        // Location 1: Before routing runs. Can influence request before routing runs.
        app.UseHttpMethodOverride();

        app.UseRouting();

        // Location 2: After routing runs. Middleware can match based on metadata.
        app.Use(next => context =>
        {
            var endpoint = context.GetEndpoint();
            if (endpoint?.Metadata.GetMetadata<AuditPolicyAttribute>()?.NeedsAudit
                                                                            == true)
            {
                Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}");
            }

            return next(context);
        });

        app.UseEndpoints(endpoints =>
        {         
            endpoints.MapGet("/", async context =>
            {
                await context.Response.WriteAsync("Hello world!");
            });

            // Using metadata to configure the audit policy.
            endpoints.MapGet("/sensitive", async context =>
            {
                await context.Response.WriteAsync("sensitive data");
            })
            .WithMetadata(new AuditPolicyAttribute(needsAudit: true));
        });

    } 
}

public class AuditPolicyAttribute : Attribute
{
    public AuditPolicyAttribute(bool needsAudit)
    {
        NeedsAudit = needsAudit;
    }

    public bool NeedsAudit { get; }
}

Předchozí příklad ukazuje dva důležité koncepty:

  • Middleware se může spustit před UseRouting úpravou dat, se kterými směrování pracuje.
  • Middleware se může spustit mezi UseRouting směrováním a UseEndpoints zpracovat výsledky směrování před spuštěním koncového bodu.
    • Middleware, který běží mezi UseRouting a UseEndpoints:
      • Obvykle kontroluje metadata, aby porozuměla koncovým bodům.
      • Často provádí rozhodnutí o zabezpečení, jak to dělá UseAuthorization a UseCors.
    • Kombinace middlewaru a metadat umožňuje konfigurovat zásady pro jednotlivé koncové body.

Předchozí kód ukazuje příklad vlastního middlewaru, který podporuje zásady pro jednotlivé koncové body. Middleware zapíše protokol auditu přístupu k citlivým datům do konzoly. Middleware je možné nakonfigurovat tak, aby auditoval koncový bod s AuditPolicyAttribute metadaty. Tato ukázka ukazuje způsob vyjádření výslovného souhlasu, kdy se auditují jenom koncové body označené jako citlivé. Tuto logiku je možné definovat obráceně, auditovat všechno, co není označené jako bezpečné, například. Systém metadat koncového bodu je flexibilní. Tato logika by mohla být navržena jakýmkoli způsobem, který vyhovuje případu použití.

Předchozí vzorový kód je určený k předvedení základních konceptů koncových bodů. Ukázka není určená pro produkční použití. Ucelenější verze middlewaru protokolu auditu:

  • Přihlaste se k souboru nebo databázi.
  • Uveďte podrobnosti, jako je uživatel, IP adresa, název citlivého koncového bodu a další.

Metadata zásad auditu jsou definována AuditPolicyAttribute jako Attribute jednodušší použití s architekturami založenými na třídách, jako jsou kontrolery a SignalR. Při použití trasy ke kódu:

  • Metadata jsou připojena pomocí rozhraní API tvůrce.
  • Architektury založené na třídách zahrnují všechny atributy odpovídající metody a třídy při vytváření koncových bodů.

Osvědčenými postupy pro typy metadat je definovat buď jako rozhraní, nebo atributy. Rozhraní a atributy umožňují opakované použití kódu. Systém metadat je flexibilní a neukládá žádná omezení.

Porovnání middlewaru terminálu a směrování

Následující ukázka kódu kontrastuje pomocí middlewaru se směrováním:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    // Approach 1: Writing a terminal middleware.
    app.Use(next => async context =>
    {
        if (context.Request.Path == "/")
        {
            await context.Response.WriteAsync("Hello terminal middleware!");
            return;
        }

        await next(context);
    });

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        // Approach 2: Using routing.
        endpoints.MapGet("/Movie", async context =>
        {
            await context.Response.WriteAsync("Hello routing!");
        });
    });
}

Styl middlewaru zobrazeného s terminálovým middlewarem Approach 1: . Říká se tomu middleware terminálu, protože dělá odpovídající operaci:

  • Odpovídající operace v předchozí ukázce je Path == "/" pro middleware a Path == "/Movie" pro směrování.
  • Když je shoda úspěšná, spustí některé funkce a vrátí se místo vyvolání middlewaru next .

Říká se tomu middleware terminálu, protože ukončí hledání, spustí některé funkce a vrátí se.

Porovnání middlewaru terminálu a směrování:

  • Oba přístupy umožňují ukončení kanálu zpracování:
    • Middleware ukončí kanál tím, že se místo vyvolání next.
    • Koncové body jsou vždy terminálové.
  • Middleware terminálu umožňuje umístění middlewaru na libovolné místo v kanálu:
  • Middleware terminálu umožňuje libovolnému kódu určit, kdy middleware odpovídá:
    • Vlastní kód pro porovnávání tras může být podrobný a obtížně zapisuje správně.
    • Směrování poskytuje jednoduchá řešení pro typické aplikace. Většina aplikací nevyžaduje vlastní kód pro porovnávání tras.
  • Rozhraní koncových bodů s middlewarem, jako UseAuthorization je například a UseCors.
    • Použití middlewaru terminálu s UseAuthorization autorizačním systémem nebo UseCors vyžaduje ruční propojení se systémem autorizace.

Koncový bod definuje obojí:

  • Delegát na zpracování požadavků.
  • Kolekce libovolných metadat. Metadata se používají k implementaci průřezových aspektů na základě zásad a konfigurace připojených k jednotlivým koncovým bodům.

Terminálový middleware může být efektivní nástroj, ale může vyžadovat:

  • Značné množství kódování a testování.
  • Ruční integrace s jinými systémy za účelem dosažení požadované úrovně flexibility.

Před zápisem middlewaru terminálu zvažte integraci se směrováním.

Existující middleware terminálu, který se integruje s mapou , nebo MapWhen se obvykle dá převést na koncový bod podporující směrování. MapHealthChecks ukazuje vzor pro router-ware:

Následující kód ukazuje použití MapHealthChecks:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    // Matches request to an endpoint.
    app.UseRouting();

    // Endpoint aware middleware. 
    // Middleware can use metadata from the matched endpoint.
    app.UseAuthentication();
    app.UseAuthorization();

    // Execute the matched endpoint.
    app.UseEndpoints(endpoints =>
    {
        // Configure the Health Check endpoint and require an authorized user.
        endpoints.MapHealthChecks("/healthz").RequireAuthorization();

        // Configure another endpoint, no authorization requirements.
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

Předchozí ukázka ukazuje, proč je vrácení objektu tvůrce důležité. Vrácení objektu tvůrce umožňuje vývojáři aplikací konfigurovat zásady, jako je autorizace pro koncový bod. V tomto příkladu nemá middleware kontroly stavu žádnou přímou integraci se systémem autorizace.

Systém metadat byl vytvořen v reakci na problémy zjištěné autory rozšiřitelnosti pomocí middlewaru terminálu. Pro každý middleware je problematické implementovat vlastní integraci s autorizačním systémem.

Porovnávání adres URL

  • Je proces, kterým směrování odpovídá příchozímu požadavku na koncový bod.
  • Je založená na datech v cestě URL a hlavičkách.
  • Můžete ho rozšířit, abyste zvážili všechna data v požadavku.

Když se spustí middleware směrování, nastaví Endpoint hodnotu požadavku na funkci HttpContext požadavku z aktuálního požadavku:

Middleware se spustí po spuštění middlewaru směrování, který může zkontrolovat koncový bod a provést akci. Například autorizační middleware může probrat kolekci metadat koncového bodu pro zásady autorizace. Po spuštění veškerého middlewaru v kanálu zpracování požadavků se vyvolá delegát vybraného koncového bodu.

Systém směrování ve směrování koncového bodu zodpovídá za veškerá rozhodnutí o odesílání. Vzhledem k tomu, že middleware používá zásady na základě vybraného koncového bodu, je důležité, aby:

  • Jakékoli rozhodnutí, které může ovlivnit odesílání nebo použití zásad zabezpečení, se provádí v rámci systému směrování.

Upozorňující

V případě zpětné kompatibility jsou při spuštění delegáta koncového bodu kontroleru nebo Razor stránky vlastnosti RouteContext.RouteData nastaveny na odpovídající hodnoty na základě dosud provedeného zpracování požadavků.

Typ RouteContext bude v budoucí verzi označen jako zastaralý:

  • Migrovat RouteData.Values na HttpRequest.RouteValues.
  • Migrujte RouteData.DataTokens a načtěte IDataTokensMetadata z metadat koncového bodu.

Porovnávání adres URL funguje v konfigurovatelné sadě fází. V každé fázi je výstup sadou shod. Sadu shod lze dále zúžit v další fázi. Implementace směrování nezaručuje pořadí zpracování pro odpovídající koncové body. Všechny možné shody se zpracovávají najednou. Fáze porovnávání adres URL probíhají v následujícím pořadí. ASP.NET Core:

  1. Zpracovává cestu URL vůči sadě koncových bodů a jejich šablon tras a shromažďuje všechny shody.
  2. Vezme předchozí seznam a odebere shody, které selžou s použitými omezeními trasy.
  3. Vezme předchozí seznam a odebere shody, které selžou sadu instancí MatcherPolicy .
  4. Pomocí endpointSelectoru provede konečné rozhodnutí z předchozího seznamu.

Seznam koncových bodů má prioritu podle následujících:

Všechny odpovídající koncové body se zpracovávají v každé fázi, dokud se nedosáhne EndpointSelector . Jedná se EndpointSelector o konečnou fázi. Jako nejlepší shodu zvolí koncový bod s nejvyšší prioritou. Pokud existují jiné shody se stejnou prioritou jako nejlepší shoda, vyvolá se výjimka nejednoznačné shody.

Priorita trasy se vypočítá na základě konkrétnější šablony trasy, která má vyšší prioritu. Představte si například šablony /hello a /{message}:

  • Obě odpovídají cestě /helloURL .
  • /hello je konkrétnější a proto vyšší priorita.

Obecně platí, že priorita trasy má dobrou úlohu při výběru nejvhodnější shody pro druhy schémat adres URL používaných v praxi. Používejte Order pouze v případě potřeby, abyste se vyhnuli nejednoznačnosti.

Vzhledem k druhům rozšiřitelnosti poskytované směrováním není možné, aby směrovací systém předem počítaly nejednoznačné trasy. Představte si příklad, jako jsou šablony /{message:alpha} tras a /{message:int}:

  • Omezení alpha odpovídá pouze abecedním znakům.
  • Omezení int odpovídá pouze číslu.
  • Tyto šablony mají stejnou prioritu tras, ale obě adresy URL se neshodují.
  • Pokud systém směrování nahlásil při spuštění nejednoznačnosti, zablokoval by tento platný případ použití.

Upozorňující

Pořadí operací uvnitř UseEndpoints nemá vliv na chování směrování, s jednou výjimkou. MapControllerRoute a MapAreaRoute automaticky přiřadí hodnotu objednávky ke svým koncovým bodům na základě pořadí, ve které jsou vyvolány. To simuluje dlouhodobé chování kontrolerů bez systému směrování, který poskytuje stejné záruky jako starší implementace směrování.

Ve starší implementaci směrování je možné implementovat rozšiřitelnost směrování, která má závislost na pořadí, ve kterém se trasy zpracovávají. Směrování koncových bodů v ASP.NET Core 3.0 a novější:

  • Nemá koncept tras.
  • Neposkytuje záruky objednávání. Všechny koncové body se zpracovávají najednou.

Priorita šablony trasy a pořadí výběru koncového bodu

Priorita šablony trasy je systém, který každé šabloně trasy přiřadí hodnotu na základě toho, jak je konkrétní. Priorita šablony trasy:

  • Vyhne se nutnosti upravovat pořadí koncových bodů v běžných případech.
  • Snaží se shodovat s očekáváními obecného chování směrování.

Představte si například šablony /Products/List a /Products/{id}. Bylo by rozumné předpokládat, že /Products/List je lepší shoda než /Products/{id} pro cestu /Products/ListURL . To funguje, protože literálový segment /List je považován za lepší prioritu než segment /{id}parametru .

Podrobnosti o tom, jak funguje priorita, jsou svázány s tím, jak se definují šablony tras:

  • Šablony s více segmenty jsou považovány za konkrétnější.
  • Segment s literálovým textem se považuje za konkrétnější než segment parametru.
  • Segment parametru s omezením se považuje za konkrétnější než jeden bez.
  • Složitý segment se považuje za specifický jako segment parametru s omezením.
  • Parametry pro zachytávání jsou nejmíň specifické. Důležité informaceoch

Referenční informace o přesných hodnotách najdete ve zdrojovém kódu na GitHubu .

Koncepty generování adres URL

Generování adres URL:

  • Je proces, pomocí kterého směrování může vytvořit cestu URL na základě sady hodnot tras.
  • Umožňuje logické oddělení mezi koncovými body a adresami URL, které k nim přistupují.

Směrování koncových bodů zahrnuje LinkGenerator rozhraní API. LinkGenerator je jednoúčelová služba, která je k dispozici z DI. Rozhraní LinkGenerator API se dá použít mimo kontext spuštěného požadavku. Mvc.IUrlHelper a scénáře, které se spoléhají , IUrlHelperjako jsou pomocné rutiny značek, pomocné rutiny HTML a výsledky akcí, používají LinkGenerator rozhraní API interně k poskytování možností generování odkazů.

Generátor odkazů je podporován konceptem adresního a adresního schématu. Schéma adres je způsob, jak určit koncové body, které by se měly zvážit pro generování propojení. Například scénáře názvu trasy a směrovacích hodnot, které mnoho uživatelů znáte z kontrolerů a Razor Stránky se implementují jako schéma adres.

Generátor odkazů může odkazovat na kontrolery a Razor stránky pomocí následujících rozšiřujících metod:

Přetížení těchto metod přijímají argumenty, které zahrnují HttpContext. Tyto metody jsou funkčně ekvivalentní url.Action a Url.Page, ale nabízejí další flexibilitu a možnosti.

Metody GetPath* jsou nejvíce podobné Url.Action a Url.Pagev tom, že vygenerují identifikátor URI obsahující absolutní cestu. Metody GetUri* vždy generují absolutní identifikátor URI obsahující schéma a hostitele. Metody, které přijímají HttpContext vygenerování identifikátoru URI v kontextu prováděcího požadavku. Hodnoty okolní trasy, základní cesta URL, schéma a hostitel ze spuštěného požadavku se použijí, pokud je nepřepíšete.

LinkGenerator je volána s adresou. Generování identifikátoru URI probíhá ve dvou krocích:

  1. Adresa je svázaná se seznamem koncových bodů, které odpovídají adrese.
  2. Vyhodnocuje se RoutePattern každý koncový bod, dokud se nenajde vzor trasy, který odpovídá zadaným hodnotám. Výsledný výstup se zkombinuje s ostatními částmi identifikátoru URI zadanými do generátoru propojení a vrátí se.

Metody poskytované standardními možnostmi LinkGenerator generování propojení pro libovolný typ adresy. Nejpohodlnější způsob použití generátoru propojení je prostřednictvím rozšiřujících metod, které provádějí operace pro konkrétní typ adresy:

Extension – metoda Popis
GetPathByAddress Vygeneruje identifikátor URI s absolutní cestou na základě zadaných hodnot.
GetUriByAddress Vygeneruje absolutní identifikátor URI na základě zadaných hodnot.

Upozorňující

Věnujte pozornost následujícím důsledkům volání LinkGenerator metod:

  • V konfiguraci aplikace používejte GetUri* metody rozšíření s opatrností, která neověřuje hlavičku Host příchozích požadavků. Pokud hlavička Host příchozích požadavků není ověřená, může být nedůvěryhodný vstup požadavku odeslán zpět klientovi v identifikátorech URI v zobrazení nebo stránce. Doporučujeme, aby všechny produkční aplikace nakonfigurovaly svůj server tak, aby ověřovaly hlavičku Host proti známým platným hodnotám.

  • Používejte LinkGenerator s opatrností v middlewaru v kombinaci s Map nebo MapWhen. Map* změní základní cestu prováděcího požadavku, která má vliv na výstup generování propojení. LinkGenerator Všechna rozhraní API umožňují zadat základní cestu. Zadejte prázdnou Map* základní cestu, která vrátí zpět vliv na generování propojení.

Příklad middlewaru

V následujícím příkladu middleware používá LinkGenerator rozhraní API k vytvoření odkazu na metodu akce, která obsahuje seznam produktů pro ukládání. Použití generátoru odkazů vložením do třídy a volání GenerateLink je k dispozici pro libovolnou třídu v aplikaci:

public class ProductsLinkMiddleware
{
    private readonly LinkGenerator _linkGenerator;

    public ProductsLinkMiddleware(RequestDelegate next, LinkGenerator linkGenerator)
    {
        _linkGenerator = linkGenerator;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        var url = _linkGenerator.GetPathByAction("ListProducts", "Store");

        httpContext.Response.ContentType = "text/plain";

        await httpContext.Response.WriteAsync($"Go to {url} to see our products.");
    }
}

Referenční informace k šabloně trasy

Tokeny v rámci {} definují parametry trasy, které jsou vázané, pokud se trasa shoduje. V segmentu trasy je možné definovat více než jeden parametr trasy, ale parametry trasy musí být oddělené hodnotou literálu. Například {controller=Home}{action=Index} není platná trasa, protože neexistuje žádná hodnota literálu mezi {controller} a {action}. Parametry trasy musí mít název a mohou mít zadané další atributy.

Literál jiný než směrovací parametry (například {id}) a oddělovač / cesty musí odpovídat textu v adrese URL. Porovnávání textu nerozlišuje malá a velká písmena a vychází z dekódované reprezentace cesty adresy URL. Chcete-li se shodovat s oddělovačem { parametru trasy literálu nebo }, uchytáte oddělovač opakováním znaku. Například {{ nebo }}.

Hvězdička * nebo dvojitá hvězdička **:

  • Lze použít jako předponu parametru trasy pro vazbu na rest identifikátor URI.
  • Nazývají se parametry catch-all. Příklad: blog/{**slug}
    • Odpovídá libovolnému identifikátoru /blog URI, který začíná a má za ním libovolnou hodnotu.
    • Následující hodnota /blog je přiřazena ke slug směrovací hodnotě.

Upozorňující

Parametr catch-all může nesprávně odpovídat trasám kvůli chybě při směrování. Aplikace ovlivněné touto chybou mají následující charakteristiky:

  • Například trasa pro zachytávání– vše {**slug}"
  • Trasa catch-all neodpovídá požadavkům, které by se měly shodovat.
  • Odebráním jiných tras začne fungovat zachytávání všech tras.

Podívejte se na chyby GitHubu 18677 a 16579 , například případy, které tuto chybu narazily.

Oprava výslovného souhlasu s touto chybou je obsažená v sadě .NET Core 3.1.301 SDK a novějších verzích. Následující kód nastaví interní přepínač, který tuto chybu opraví:

public static void Main(string[] args)
{
   AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior", 
                         true);
   CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.

Parametry catch-all mohou také odpovídat prázdnému řetězci.

Parametr catch-all umisťuje příslušné znaky při použití trasy k vygenerování adresy URL, včetně znaků oddělovače / cest. Například trasa foo/{*path} s hodnotami { path = "my/path" } trasy generuje foo/my%2Fpath. Všimněte si řídicího lomítka. Pokud chcete znaky oddělovače cest odezvy použít předponu parametru ** trasy. Trasa foo/{**path} s { path = "my/path" } vygenerovanými .foo/my/path

Vzory adres URL, které se pokoušejí zachytit název souboru s volitelnou příponou souboru, mají další důležité informace. Představte si například šablonu files/{filename}.{ext?}. Když hodnoty obou filename hodnot existují ext , naplní se obě hodnoty. Pokud v adrese URL existuje jenom hodnota filename , trasa se shoduje, protože koncové hodnoty . jsou volitelné. Následující adresy URL odpovídají této trase:

  • /files/myFile.txt
  • /files/myFile

Parametry trasy můžou mít výchozí hodnoty určené zadáním výchozí hodnoty za názvem parametru odděleným symbolem rovná se (=). Například {controller=Home} definuje Home jako výchozí hodnotu pro controller. Výchozí hodnota se použije, pokud v adrese URL parametru není žádná hodnota. Parametry trasy jsou volitelné připojením otazníku (?) na konec názvu parametru. Například id?. Rozdíl mezi volitelnými hodnotami a výchozími parametry trasy je:

  • Parametr trasy s výchozí hodnotou vždy vytvoří hodnotu.
  • Volitelný parametr má hodnotu pouze v případě, že je hodnota poskytována adresou URL požadavku.

Parametry trasy můžou mít omezení, která musí odpovídat hodnotě trasy vázané z adresy URL. Přidání : a omezení názvu za názvem parametru trasy určuje vložené omezení parametru trasy. Pokud omezení vyžaduje argumenty, jsou za názvem omezení uzavřeny v závorkách (...) . Více vložených omezení lze zadat připojením jiného : názvu a názvu omezení.

Název omezení a argumenty se předávají službě IInlineConstraintResolver , aby se vytvořila instance IRouteConstraint , která se má použít při zpracování adresy URL. Například šablona blog/{article:minlength(10)} trasy určuje minlength omezení s argumentem 10. Další informace o omezeních tras a seznamu omezení poskytovaných architekturou najdete v části Odkaz na omezení trasy.

Parametry trasy mohou mít také transformátory parametrů. Transformátory parametrů transformují hodnotu parametru při generování odkazů a odpovídajících akcí a stránek na adresy URL. Stejně jako omezení lze transformátory parametrů přidat do parametru trasy přidáním : názvu a transformátoru za název parametru trasy. Například šablona blog/{article:slugify} trasy určuje slugify transformátor. Další informace o transformátorech parametrů naleznete v části Odkaz transformátoru parametrů.

Následující tabulka ukazuje ukázkové šablony tras a jejich chování:

Šablona trasy Příklad odpovídajícího identifikátoru URI Identifikátor URI požadavku...
hello /hello Odpovídá pouze jedné cestě /hello.
{Page=Home} / Odpovídá a nastaví Page na Home.
{Page=Home} /Contact Odpovídá a nastaví Page na Contact.
{controller}/{action}/{id?} /Products/List Mapuje se na Products kontroler a List akci.
{controller}/{action}/{id?} /Products/Details/123 Mapuje se Products na kontroler a Details akci s nastavenouid na hodnotu 123.
{controller=Home}/{action=Index}/{id?} / Mapuje se na kontroler a Index metoduHome. Vlastnost id je ignorována.
{controller=Home}/{action=Index}/{id?} /Products Mapuje se na kontroler a Index metoduProducts. Vlastnost id je ignorována.

Použití šablony je obecně nejjednodušším přístupem ke směrování. Omezení a výchozí hodnoty lze zadat také mimo šablonu trasy.

Komplexní segmenty

Komplexní segmenty se zpracovávají pomocí odpovídajících oddělovačů literálů zprava doleva způsobem, který není greedy . Jedná se například [Route("/a{b}c{d}")] o složitý segment. Složité segmenty fungují určitým způsobem, který je potřeba pochopit, aby je bylo možné úspěšně použít. Příklad v této části ukazuje, proč složité segmenty skutečně fungují dobře, když se text oddělovače nezobrazí uvnitř hodnot parametrů. Použití regulárního výrazu a ruční extrahování hodnot je potřeba pro složitější případy.

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Toto je souhrn kroků, které směrování provádí pomocí šablony /a{b}c{d} a cesty /abcdURL . Slouží | k vizualizaci fungování algoritmu:

  • První literál, zprava doleva, je c. Hledá se tedy /abcd zprava a najde /ab|c|d.
  • Vše napravo (d) se teď shoduje s parametrem {d}trasy .
  • Další literál, zprava doleva, je a. Takže /ab|c|d je prohledáno od místa, kde jsme skončili, pak a je nalezen /|a|b|c|d.
  • Hodnota vpravo (b) se teď shoduje s parametrem {b}trasy .
  • Neexistuje žádný zbývající text a žádná zbývající šablona trasy, takže se jedná o shodu.

Tady je příklad záporného případu, který používá stejnou šablonu /a{b}c{d} a cestu /aabcdURL . Slouží | k vizualizaci fungování algoritmu. Tento případ není shoda, která je vysvětlená stejným algoritmem:

  • První literál, zprava doleva, je c. Hledá se tedy /aabcd zprava a najde /aab|c|d.
  • Vše napravo (d) se teď shoduje s parametrem {d}trasy .
  • Další literál, zprava doleva, je a. Takže /aab|c|d je prohledáno od místa, kde jsme skončili, pak a je nalezen /a|a|b|c|d.
  • Hodnota vpravo (b) se teď shoduje s parametrem {b}trasy .
  • V tomto okamžiku existuje zbývající text a, ale algoritmus vyčeráhl šablonu trasy, aby parsovala, takže se nejedná o shodu.

Vzhledem k tomu, že odpovídající algoritmus není greedy:

  • Odpovídá nejmenšímu množství textu, který je možné v každém kroku.
  • Případ, kdy se hodnota oddělovače zobrazí uvnitř hodnot parametrů, způsobí, že se neshoduje.

Regulární výrazy poskytují mnohem větší kontrolu nad jejich odpovídajícím chováním.

Párování Greedy, označované také jako opožděné párování, odpovídá největšímu možnému řetězci. Greedy odpovídá nejmenšímu možnému řetězci.

Referenční informace o omezení trasy

Omezení směrování se provádějí, když došlo ke shodě s příchozí adresou URL a cesta url se tokenizuje do hodnot tras. Omezení tras obecně kontrolují hodnotu trasy přidruženou prostřednictvím šablony trasy a ověřte, jestli je hodnota přijatelná, nebo nepravdivá. Některá omezení trasy používají data mimo hodnotu trasy a zvažují, jestli je možné požadavek směrovat. Například HttpMethodRouteConstraint může přijmout nebo odmítnout požadavek na základě jeho příkazu HTTP. Omezení se používají v požadavcích směrování a generování propojení.

Upozorňující

Nepoužívejte omezení pro ověřování vstupu. Pokud se pro ověření vstupu používají omezení, výsledkem neplatného 404 vstupu je odpověď Nenalezena. Neplatný vstup by měl vytvořit chybný 400 požadavek s příslušnou chybovou zprávou. Omezení tras se používají k nejednoznačnosti podobných tras, nikoli k ověření vstupů pro konkrétní trasu.

Následující tabulka ukazuje ukázková omezení směrování a jejich očekávané chování:

omezení Příklad Příklady shod Notes
int {id:int} 123456789, -123456789 Odpovídá libovolnému celočíselnému číslu.
bool {active:bool} true, FALSE Shody true nebo false. Nerozlišují se malá a velká písmena
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Odpovídá platné DateTime hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
decimal {price:decimal} 49.99, -1,000.01 Odpovídá platné decimal hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
double {weight:double} 1.234, -1,001.01e8 Odpovídá platné double hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
float {weight:float} 1.234, -1,001.01e8 Odpovídá platné float hodnotě v invariantní jazykové verzi. Viz předchozí upozornění.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Odpovídá platné Guid hodnotě.
long {ticks:long} 123456789, -123456789 Odpovídá platné long hodnotě.
minlength(value) {username:minlength(4)} Rick Řetězec musí mít alespoň 4 znaky.
maxlength(value) {filename:maxlength(8)} MyFile Řetězec nesmí být delší než 8 znaků.
length(length) {filename:length(12)} somefile.txt Řetězec musí mít přesně 12 znaků.
length(min,max) {filename:length(8,16)} somefile.txt Řetězec musí mít maximálně 8 znaků a nesmí být delší než 16 znaků.
min(value) {age:min(18)} 19 Celočíselná hodnota musí být alespoň 18.
max(value) {age:max(120)} 91 Celočíselná hodnota nesmí být větší než 120.
range(min,max) {age:range(18,120)} 91 Celočíselná hodnota musí být alespoň 18, ale nesmí být větší než 120.
alpha {name:alpha} Rick Řetězec musí obsahovat jeden nebo více abecedních znaků a-z a nerozlišuje velká a malá písmena.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 Řetězec musí odpovídat regulárnímu výrazu. Podívejte se na tipy k definování regulárního výrazu.
required {name:required} Rick Používá se k vynucení toho, aby během generování adresy URL byla přítomna hodnota, která není parametrem.

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Pro jeden parametr lze použít více omezení oddělených dvojtečkami. Například následující omezení omezuje parametr na celočíselnou hodnotu 1 nebo vyšší:

[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }

Upozorňující

Omezení směrování, která ověřují adresu URL a jsou převedeny na typ CLR, vždy používají neutrální jazykovou verzi. Například převod na typ int CLR nebo DateTime. Tato omezení předpokládají, že adresa URL není lokalizovatelná. Omezení tras poskytovaná architekturou nemění hodnoty uložené v hodnotách tras. Všechny hodnoty směrování parsované z adresy URL se ukládají jako řetězce. Omezení se například pokusí převést hodnotu trasy na hodnotu float, ale převedená hodnota se použije pouze k ověření, float že se dá převést na plovoucí hodnotu.

Regulární výrazy v omezeních

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Regulární výrazy lze zadat jako vložená omezení pomocí regex(...) omezení trasy. Metody v rodině MapControllerRoute také přijímají literál objektu omezení. Pokud se tento formulář použije, řetězcové hodnoty se interpretují jako regulární výrazy.

Následující kód používá omezení vložených regulárních výrazů:

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
        context => 
        {
            return context.Response.WriteAsync("inline-constraint match");
        });
 });

Následující kód používá literál objektu k určení omezení regulárního výrazu:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "people",
        pattern: "People/{ssn}",
        constraints: new { ssn = "^\\d{3}-\\d{2}-\\d{4}$", },
        defaults: new { controller = "People", action = "List", });
});

Architektura ASP.NET Core přidává RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant do konstruktoru regulárních výrazů. Podívejte RegexOptions se na popis těchto členů.

Regulární výrazy používají oddělovače a tokeny podobné těm používaným směrováním a jazykem C#. Tokeny regulárních výrazů musí být uchycené. Chcete-li použít regulární výraz ^\d{3}-\d{2}-\d{4}$ v vložené omezení, použijte jednu z následujících možností:

  • Nahraďte \ znaky zadané v řetězci jako \\ znaky ve zdrojovém souboru jazyka C#, aby bylo možné řídicí znak řetězce utéct \ .
  • Doslovné řetězcové literály.

Chcete-li uvozovat znaky {oddělovače parametrů směrování , [}]zdvojnásobit znaky ve výrazu, {{například , }}, , . ]][[ Následující tabulka ukazuje regulární výraz a jeho řídicí verzi:

Regulární výraz Řídicí regulární výraz
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Regulární výrazy používané při směrování často začínají znakem ^ a odpovídají počáteční pozici řetězce. Výrazy často končí znakem $ a odpovídají konci řetězce. Znaky ^ a $ zajistěte, aby regulární výraz odpovídal celé hodnotě parametru trasy. ^ Bez znaků a $ znaků regulární výraz odpovídá jakémukoli podřetězci v řetězci, což je často nežádoucí. Následující tabulka obsahuje příklady a vysvětluje, proč se shodují nebo se neshodují:

Výraz String Párování Komentář
[a-z]{2} dobrý den Ano Shoda podřetěžce
[a-z]{2} 123abc456 Ano Shoda podřetěžce
[a-z]{2} mz Ano Odpovídá výrazu
[a-z]{2} MZ Ano Nerozlišuje se malá a velká písmena.
^[a-z]{2}$ dobrý den No Viz ^ a $ výše
^[a-z]{2}$ 123abc456 No Viz ^ a $ výše

Další informace o syntaxi regulárních výrazů naleznete v tématu Regulární výrazy rozhraní .NET Framework.

Chcete-li omezit parametr na známou sadu možných hodnot, použijte regulární výraz. Například {action:regex(^(list|get|create)$)} odpovídá pouze hodnotě action trasy do list, getnebo create. Pokud je řetězec předán do slovníku omezení, je řetězec ^(list|get|create)$ ekvivalentní. Omezení, která se předávají ve slovníku omezení, která neodpovídají některému ze známých omezení, se také považují za regulární výrazy. Omezení předaná v šabloně, která neodpovídají některému ze známých omezení, se považují za regulární výrazy.

Vlastní omezení trasy

Vlastní omezení trasy je možné vytvořit implementací IRouteConstraint rozhraní. Rozhraní IRouteConstraint obsahuje Match, který vrátí true , pokud je omezení splněno a false jinak.

Vlastní omezení trasy jsou zřídka nutná. Před implementací vlastního omezení trasy zvažte alternativy, jako je například vazba modelu.

Složka ASP.NET Core Constraints poskytuje dobré příklady vytváření omezení. Například GuidRouteConstraint.

Pokud chcete použít vlastní IRouteConstrainttyp omezení trasy, musí být zaregistrovaný v kontejneru služby v aplikaci ConstraintMap . A ConstraintMap je slovník, který mapuje klíče omezení směrování na IRouteConstraint implementace, které tato omezení ověřují. Aplikaci ConstraintMap je možné aktualizovat buď jako Startup.ConfigureServices součást služeb. AddRouting call or by configuring RouteOptions directly with services.Configure<RouteOptions>. Příklad:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddRouting(options =>
    {
        options.ConstraintMap.Add("customName", typeof(MyCustomConstraint));
    });
}

Předchozí omezení se použije v následujícím kódu:

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    // GET /api/test/3
    [HttpGet("{id:customName}")]
    public IActionResult Get(string id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // GET /api/test/my/3
    [HttpGet("my/{id:customName}")]
    public IActionResult Get(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

MyDisplayRouteInfo poskytuje balíček NuGet Rick.Docs.Samples.RouteInfo a zobrazí informace o trase.

Implementace MyCustomConstraint brání 0 použití parametru trasy:

class MyCustomConstraint : IRouteConstraint
{
    private Regex _regex;

    public MyCustomConstraint()
    {
        _regex = new Regex(@"^[1-9]*$",
                            RegexOptions.CultureInvariant | RegexOptions.IgnoreCase,
                            TimeSpan.FromMilliseconds(100));
    }
    public bool Match(HttpContext httpContext, IRouter route, string routeKey,
                      RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (values.TryGetValue(routeKey, out object value))
        {
            var parameterValueString = Convert.ToString(value,
                                                        CultureInfo.InvariantCulture);
            if (parameterValueString == null)
            {
                return false;
            }

            return _regex.IsMatch(parameterValueString);
        }

        return false;
    }
}

Upozorňující

Při zpracování System.Text.RegularExpressions nedůvěryhodného vstupu předejte vypršení časového limitu. Uživatel se zlými úmysly může poskytnout vstup, který RegularExpressions způsobí útok na dostupnost služby. ASP.NET rozhraní API architektury Core, která používají RegularExpressions vypršení časového limitu.

Předchozí kód:

  • Zabraňuje 0 v {id} segmentu trasy.
  • Ukazuje se, že poskytuje základní příklad implementace vlastního omezení. Neměla by se používat v produkční aplikaci.

Následující kód je lepším přístupem k tomu, aby se zabránilo zpracování obsahujícího id 0 :

[HttpGet("{id}")]
public IActionResult Get(string id)
{
    if (id.Contains('0'))
    {
        return StatusCode(StatusCodes.Status406NotAcceptable);
    }

    return ControllerContext.MyDisplayRouteInfo(id);
}

Předchozí kód má oproti přístupu následující výhody MyCustomConstraint :

  • Nevyžaduje vlastní omezení.
  • Vrátí popisnější chybu, pokud parametr trasy obsahuje 0.

Odkaz na transformátor parametrů

Transformátory parametrů:

Například vlastní slugify transformátor parametru ve vzoru blog\{article:slugify} trasy s Url.Action(new { article = "MyTestArticle" }) vygenerovanými blog\my-test-article.

Zvažte následující IOutboundParameterTransformer implementaci:

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) { return null; }

        return Regex.Replace(value.ToString(), 
                             "([a-z])([A-Z])",
                             "$1-$2",
                             RegexOptions.CultureInvariant,
                             TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
    }
}

Chcete-li použít transformátor parametrů ve vzoru trasy, nakonfigurujte ho pomocí :ConstraintMap Startup.ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddRouting(options =>
    {
        options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
    });
}

Architektura ASP.NET Core používá transformátory parametrů k transformaci identifikátoru URI, kde se koncový bod překládá. Například transformátory parametrů transformují směrovací hodnoty použité ke shodě area, controller, actiona page.

routes.MapControllerRoute(
    name: "default",
    template: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");

U předchozí šablony trasy se akce SubscriptionManagementController.GetAll shoduje s identifikátorem URI /subscription-management/get-all. Transformátor parametrů nemění směrovací hodnoty použité k vygenerování propojení. Například Url.Action("GetAll", "SubscriptionManagement") výstupy /subscription-management/get-all.

ASP.NET Core poskytuje konvence rozhraní API pro použití transformátorů parametrů se generovanými trasami:

Referenční informace ke generování adres URL

Tato část obsahuje odkaz na algoritmus implementovaný generováním adres URL. V praxi používají nejsložitější příklady generování adres URL kontrolery nebo Razor stránky. Další informace najdete v tématu Směrování v kontrolerů .

Proces generování adresy URL začíná voláním LinkGenerator.GetPathByAddress nebo podobné metody. Metoda je poskytována s adresou, sadou hodnot tras a volitelně informace o aktuálním požadavku z HttpContext.

Prvním krokem je použití adresy k vyřešení sady kandidátských koncových bodů pomocí typu IEndpointAddressScheme<TAddress> adresy.

Jakmile sada kandidátů najde schéma adres, koncové body se objednávají a zpracovávají iterativním způsobem, dokud operace generování adresy URL nebude úspěšná. Generování adresy URL nekontroluje nejednoznačnosti, první vrácený výsledek je konečný výsledek.

Řešení potíží s generováním adres URL pomocí protokolování

Prvním krokem při generování adresy URL při řešení potíží je nastavení úrovně Microsoft.AspNetCore.Routing protokolování na TRACE. LinkGenerator zaznamenává mnoho podrobností o jeho zpracování, které může být užitečné při řešení problémů.

Podrobnosti o generování adres URL najdete v referenčních informacích ke generování adres URL.

Adresy

Adresy jsou konceptem generování adres URL, které slouží k vytvoření vazby volání do generátoru odkazů na sadu kandidátských koncových bodů.

Adresy jsou rozšiřitelný koncept, který ve výchozím nastavení obsahuje dvě implementace:

  • Jako adresu použijte název koncového bodu (string):
    • Poskytuje podobné funkce jako název trasy MVC.
    • IEndpointNameMetadata Používá typ metadat.
    • Vyřeší zadaný řetězec s metadaty všech registrovaných koncových bodů.
    • Vyvolá výjimku při spuštění, pokud více koncových bodů používá stejný název.
    • Doporučuje se pro obecné použití mimo kontrolery a Razor stránky.
  • Použití hodnot směrování (RouteValuesAddress) jako adresy:
    • Poskytuje podobné funkce jako kontrolery a Razor starší generace adres URL stránky.
    • Velmi složité rozšířit a ladit.
    • Poskytuje implementaci, kterou IUrlHelperpoužívají pomocné rutiny značek, pomocné rutiny HTML, výsledky akcí atd.

Role schématu adres spočívá v přidružení mezi adresou a odpovídajícími koncovými body podle libovolných kritérií:

  • Schéma názvu koncového bodu provádí základní vyhledávání slovníku.
  • Schéma hodnot tras má komplexní nejlepší podmnožinu algoritmu sady.

Okolní hodnoty a explicitní hodnoty

Z aktuálního požadavku směrování přistupuje ke směrovacím hodnotám aktuálního požadavku HttpContext.Request.RouteValues. Hodnoty přidružené k aktuálnímu požadavku se označují jako okolní hodnoty. Pro účely srozumitelnosti se dokumentace týká směrovacích hodnot předávaných metodám jako explicitních hodnot.

Následující příklad ukazuje okolní hodnoty a explicitní hodnoty. Poskytuje okolní hodnoty z aktuálního požadavku a explicitní hodnoty: { id = 17, }

public class WidgetController : Controller
{
    private readonly LinkGenerator _linkGenerator;

    public WidgetController(LinkGenerator linkGenerator)
    {
        _linkGenerator = linkGenerator;
    }

    public IActionResult Index()
    {
        var url = _linkGenerator.GetPathByAction(HttpContext,
                                                 null, null,
                                                 new { id = 17, });
        return Content(url);
    }

Předchozí kód:

Následující kód neposkytuje žádné okolní hodnoty a explicitní hodnoty: { controller = "Home", action = "Subscribe", id = 17, }

public IActionResult Index2()
{
    var url = _linkGenerator.GetPathByAction("Subscribe", "Home",
                                             new { id = 17, });
    return Content(url);
}

Předchozí metoda vrátí /Home/Subscribe/17

Následující kód ve návratu WidgetController /Widget/Subscribe/17:

var url = _linkGenerator.GetPathByAction("Subscribe", null,
                                         new { id = 17, });

Následující kód poskytuje kontroler z okolních hodnot v aktuálním požadavku a explicitní hodnoty: { action = "Edit", id = 17, }

public class GadgetController : Controller
{
    public IActionResult Index()
    {
        var url = Url.Action("Edit", new { id = 17, });
        return Content(url);
    }

V předchozím kódu:

  • /Gadget/Edit/17 je vrácena.
  • UrlIUrlHelperzíská .
  • Action vygeneruje adresu URL s absolutní cestou pro metodu akce. Adresa URL obsahuje zadaný action název a route hodnoty.

Následující kód poskytuje okolní hodnoty z aktuálního požadavku a explicitní hodnoty: { page = "./Edit, id = 17, }

public class IndexModel : PageModel
{
    public void OnGet()
    {
        var url = Url.Page("./Edit", new { id = 17, });
        ViewData["URL"] = url;
    }
}

Předchozí kód se nastaví url , když /Edit/17 Edit Razor Page obsahuje následující direktivu stránky:

@page "{id:int}"

Pokud stránka Upravit neobsahuje "{id:int}" šablonu trasy, url je /Edit?id=17.

Chování MVC IUrlHelper přidává kromě zde popsaných pravidel vrstvu složitosti:

  • IUrlHelper vždy poskytuje směrovací hodnoty z aktuálního požadavku jako okolní hodnoty.
  • IUrlHelper.Action vždy zkopíruje aktuální action hodnoty a controller směrovací hodnoty jako explicitní hodnoty, pokud je nepřepíše vývojář.
  • IUrlHelper.Page vždy zkopíruje aktuální page hodnotu trasy jako explicitní hodnotu, pokud ji nepřepíšete.
  • IUrlHelper.Page vždy přepíše aktuální handler hodnotu null trasy jako explicitní hodnoty, pokud ji nepřepíšete.

Uživatelé jsou často překvapeni chováním okolních hodnot, protože MVC zdánlivě nedodržuje vlastní pravidla. Z historických důvodů a z důvodu kompatibility mají určité hodnoty tras, jako actionje , controller, pagea handler mají vlastní zvláštní chování.

Ekvivalentní funkce poskytované LinkGenerator.GetPathByAction a LinkGenerator.GetPathByPage duplikuje tyto anomálie z důvodu kompatibility IUrlHelper .

Proces generování adres URL

Jakmile se najde sada kandidátských koncových bodů, algoritmus generování adres URL:

  • Zpracovává koncové body iterativním způsobem.
  • Vrátí první úspěšný výsledek.

Prvním krokem v tomto procesu je zneplatnění hodnoty trasy. Zneplatnění hodnoty trasy je proces, kterým směrování rozhodne, které hodnoty trasy z okolních hodnot se mají použít a které by se měly ignorovat. Každá okolní hodnota se považuje a buď v kombinaci s explicitními hodnotami, nebo je ignorována.

Nejlepším způsobem, jak přemýšlet o roli okolních hodnot, je, že se snaží uložit vývojáři aplikací psaní, v některých běžných případech. Scénáře, ve kterých jsou okolní hodnoty užitečné, se tradičně vztahují k MVC:

  • Při propojení s jinou akcí ve stejném řadiči není nutné zadat název kontroleru.
  • Při propojení s jiným řadičem ve stejné oblasti není nutné zadat název oblasti.
  • Při propojení se stejnou metodou akce není nutné zadávat hodnoty tras.
  • Při propojení s jinou částí aplikace nechcete přenášet hodnoty tras, které nemají v této části aplikace žádný význam.

Volání nebo LinkGenerator IUrlHelper vrácení jsou obvykle způsobená tím, že null nerozumí neplatné hodnotě trasy. Při řešení potíží s neplatnou hodnotou trasy můžete explicitně zadat více hodnot tras, abyste zjistili, jestli se tím problém vyřeší.

Neplatná hodnota trasy funguje na předpokladu, že schéma adresy URL aplikace je hierarchické s hierarchií vytvořenou zleva doprava. Představte si šablonu {controller}/{action}/{id?} směrování základního kontroleru, abyste získali intuitivní představu o tom, jak to funguje v praxi. Změna hodnoty zneplatní všechny směrovací hodnoty, které se zobrazují vpravo. To odráží předpoklad o hierarchii. Pokud má aplikace okolní hodnotu a idoperace určuje jinou hodnotu pro controller:

  • id nebude znovu použito, protože {controller} je nalevo od {id?}.

Několik příkladů demonstrujících tento princip:

  • Pokud explicitní hodnoty obsahují hodnotu pro id, okolní hodnota je id ignorována. Okolní hodnoty a controller action lze je použít.
  • Pokud explicitní hodnoty obsahují hodnotu pro action, je ignorována jakákoli okolní hodnota pro action . Lze použít okolní hodnoty controller . Pokud se explicitní hodnota pro jinou než okolní hodnota action actionpro , id hodnota se nepoužije. Pokud je explicitní hodnota action stejná jako okolní hodnota pro action, id lze použít hodnotu.
  • Pokud explicitní hodnoty obsahují hodnotu pro controller, je ignorována jakákoli okolní hodnota pro controller . Pokud se explicitní hodnota pro jinou než okolní hodnota controller controllerpro , action hodnoty a id hodnoty nebudou použity. Pokud je explicitní hodnota controller stejná jako okolní hodnota pro controller, action lze použít hodnoty a id hodnoty.

Tento proces je ještě složitější díky existenci tras atributů a vyhrazených konvenčních tras. Běžné trasy kontroleru, jako {controller}/{action}/{id?} je například určení hierarchie pomocí parametrů trasy. Pro vyhrazené konvenční trasy a trasy atributů pro kontrolery a Razor stránky:

  • Existuje hierarchie hodnot tras.
  • Nezobrazují se v šabloně.

V těchto případech generování adresy URL definuje koncept požadovaných hodnot . Koncové body vytvořené kontrolery a Razor stránkami mají zadané požadované hodnoty, které umožňují zneplatnění hodnoty trasy.

Podrobný algoritmus zneplatnění hodnoty trasy:

  • Požadované názvy hodnot se kombinují s parametry trasy a pak se zpracovávají zleva doprava.
  • Pro každý parametr se porovná okolní hodnota a explicitní hodnota:
    • Pokud je okolní hodnota a explicitní hodnota stejné, proces pokračuje.
    • Pokud je okolní hodnota přítomná a explicitní hodnota není, použije se při generování adresy URL okolní hodnota.
    • Pokud okolní hodnota není přítomná a explicitní hodnota je, zamítněte okolní hodnotu a všechny následné okolní hodnoty.
    • Pokud se nachází okolí a explicitní hodnota a obě hodnoty jsou odlišné, zamítněte okolní hodnotu a všechny následné okolní hodnoty.

V tuto chvíli je operace generování adres URL připravená k vyhodnocení omezení trasy. Sada přijatých hodnot se zkombinuje s výchozími hodnotami parametrů, které jsou k dispozici pro omezení. Pokud všechna omezení projdou, operace bude pokračovat.

Dále je možné použít akceptované hodnoty k rozšíření šablony trasy. Šablona trasy se zpracuje:

  • Zleva doprava.
  • Každý parametr má jeho přijatou hodnotu nahrazenou.
  • S následujícími zvláštními případy:
    • Pokud přijaté hodnoty chybí hodnota a parametr má výchozí hodnotu, použije se výchozí hodnota.
    • Pokud přijaté hodnoty chybí a parametr je nepovinný, zpracování pokračuje.
    • Pokud některý parametr trasy napravo od chybějícího volitelného parametru má hodnotu, operace selže.
    • Pokud je to možné, sbalí se souvislé parametry s výchozími hodnotami a volitelné parametry.

Hodnoty explicitně za předpokladu, že neodpovídají segmentu trasy, se přidají do řetězce dotazu. Následující tabulka ukazuje výsledek při použití šablony {controller}/{action}/{id?}trasy .

Okolní hodnoty Explicitní hodnoty Výsledek
controller = "Home" action = "O aplikaci" /Home/About
controller = "Home" controller = "Order", action = "About" /Order/About
controller = "Home", color = "Red" action = "O aplikaci" /Home/About
controller = "Home" action = "O produktu", barva = "Červená" /Home/About?color=Red

Problémy s neplatnou hodnotou trasy

Od ASP.NET Core 3.0 nefungují některá schémata generování adres URL použitá v dřívějších verzích ASP.NET Core s generováním adres URL dobře. Tým ASP.NET Core plánuje přidat funkce pro řešení těchto potřeb v budoucí verzi. Prozatím je nejlepším řešením použít starší směrování.

Následující kód ukazuje příklad schématu generování adres URL, které není podporováno směrováním.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute("default", 
                                     "{culture}/{controller=Home}/{action=Index}/{id?}");
    endpoints.MapControllerRoute("blog", "{culture}/{**slug}", 
                                      new { controller = "Blog", action = "ReadPost", });
});

V předchozím kódu culture se parametr trasy používá pro lokalizaci. Touha má parametr culture vždy přijmout jako okolní hodnotu. Parametr culture však není přijat jako okolní hodnota kvůli způsobu, jakým požadované hodnoty fungují:

  • "default" V šabloně culture trasy je parametr trasy vlevo od controller, takže změny controller nebudou cultureneplatné .
  • "blog" V šabloně culture trasy se parametr trasy považuje za napravo od controllerparametru trasy, který se zobrazí v požadovaných hodnotách.

Konfigurace metadat koncového bodu

Následující odkazy obsahují informace o konfiguraci metadat koncového bodu:

Porovnávání hostitelů v trasách pomocí RequireHost

RequireHost použije omezení na trasu, která vyžaduje zadaného hostitele. Parametrem RequireHost [Host] může být:

  • Hostitel: www.domain.com, odpovídá www.domain.com libovolnému portu.
  • Hostitel se zástupným znakem: *.domain.com, shody www.domain.com, subdomain.domain.comnebo www.subdomain.domain.com na libovolném portu.
  • Port: *:5000Odpovídá portu 5000 s libovolným hostitelem.
  • Hostitel a port: www.domain.com:5000 nebo *.domain.com:5000odpovídá hostiteli a portu.

Lze zadat více parametrů pomocí RequireHost nebo [Host]. Omezení odpovídá hostitelům platným pro některý z parametrů. Například [Host("domain.com", "*.domain.com")] shody domain.com, www.domain.coma subdomain.domain.com.

Následující kód používá RequireHost k vyžadování zadaného hostitele na trase:

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", context => context.Response.WriteAsync("Hi Contoso!"))
            .RequireHost("contoso.com");
        endpoints.MapGet("/", context => context.Response.WriteAsync("AdventureWorks!"))
            .RequireHost("adventure-works.com");
        endpoints.MapHealthChecks("/healthz").RequireHost("*:8080");
    });
}

Následující kód používá [Host] atribut na kontroleru k vyžadování některého ze zadaných hostitelů:

[Host("contoso.com", "adventure-works.com")]
public class ProductController : Controller
{
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Host("example.com:8080")]
    public IActionResult Privacy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

[Host] Když se atribut použije na metodu kontroleru i akce:

  • Použije se atribut akce.
  • Atribut kontroleru je ignorován.

Doprovodné materiály k výkonu pro směrování

Většina směrování se aktualizovala v ASP.NET Core 3.0, aby se zvýšil výkon.

Pokud má aplikace problémy s výkonem, směrování se často považuje za problém. Důvodem je podezření, že architektury, jako jsou kontrolery a Razor stránky, hlásí dobu strávenou uvnitř rozhraní v protokolovacích zprávách. Pokud je mezi časem hlášeným kontrolery a celkovým časem požadavku významný rozdíl:

  • Vývojáři eliminují kód aplikace jako zdroj problému.
  • Běžně se předpokládá, že příčinou je směrování.

Směrování je testované výkonem pomocí tisíců koncových bodů. Je nepravděpodobné, že by typická aplikace narazila na problém s výkonem jenom tím, že je příliš velká. Nejčastější hlavní příčinou pomalého směrování je obvykle špatně se chovající vlastní middleware.

Následující ukázka kódu ukazuje základní techniku zúžení zdroje zpoždění:

public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
    app.Use(next => async context =>
    {
        var sw = Stopwatch.StartNew();
        await next(context);
        sw.Stop();

        logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", sw.ElapsedMilliseconds);
    });

    app.UseRouting();

    app.Use(next => async context =>
    {
        var sw = Stopwatch.StartNew();
        await next(context);
        sw.Stop();

        logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", sw.ElapsedMilliseconds);
    });

    app.UseAuthorization();

    app.Use(next => async context =>
    {
        var sw = Stopwatch.StartNew();
        await next(context);
        sw.Stop();

        logger.LogInformation("Time 3: {ElapsedMilliseconds}ms", sw.ElapsedMilliseconds);
    });

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Timing test.");
        });
    });
}

Směrování podle času:

  • Prokládání každého middlewaru pomocí kopie middlewaru časování zobrazeného v předchozím kódu.
  • Přidejte jedinečný identifikátor pro korelaci dat časování s kódem.

Jedná se o základní způsob, jak zúžit zpoždění, pokud je významné, například více než 10ms. Odečítá Time 2 se od Time 1 sestav čas strávený v middlewaru UseRouting .

Následující kód používá kompaktnější přístup k předchozímu kódu časování:

public sealed class MyStopwatch : IDisposable
{
    ILogger<Startup> _logger;
    string _message;
    Stopwatch _sw;

    public MyStopwatch(ILogger<Startup> logger, string message)
    {
        _logger = logger;
        _message = message;
        _sw = Stopwatch.StartNew();
    }

    private bool disposed = false;


    public void Dispose()
    {
        if (!disposed)
        {
            _logger.LogInformation("{Message }: {ElapsedMilliseconds}ms",
                                    _message, _sw.ElapsedMilliseconds);

            disposed = true;
        }
    }
}
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
    int count = 0;
    app.Use(next => async context =>
    {
        using (new MyStopwatch(logger, $"Time {++count}"))
        {
            await next(context);
        }

    });

    app.UseRouting();

    app.Use(next => async context =>
    {
        using (new MyStopwatch(logger, $"Time {++count}"))
        {
            await next(context);
        }
    });

    app.UseAuthorization();

    app.Use(next => async context =>
    {
        using (new MyStopwatch(logger, $"Time {++count}"))
        {
            await next(context);
        }
    });

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Timing test.");
        });
    });
}

Potenciálně nákladné funkce směrování

Následující seznam obsahuje přehled o funkcích směrování, které jsou relativně drahé v porovnání se základními šablonami tras:

  • Regulární výrazy: Je možné psát regulární výrazy, které jsou složité nebo mají dlouhou dobu trvání s malým množstvím vstupu.
  • Komplexní segmenty ({x}-{y}-{z}):
    • Jsou výrazně dražší než analýza běžného segmentu cesty URL.
    • Výsledkem je přidělení mnoha dalších podřetěžců.
    • V aktualizaci výkonu směrování core 3.0 ASP.NET se neaktualizovala logika komplexního segmentu.
  • Synchronní přístup k datům: Mnoho složitých aplikací má v rámci směrování přístup k databázi. ASP.NET Core 2.2 a starší směrování nemusí poskytovat správné body rozšiřitelnosti pro podporu směrování přístupu k databázi. Například IRouteConstrainta IActionConstraint jsou synchronní. Body rozšiřitelnosti, jako MatcherPolicy jsou a EndpointSelectorContext jsou asynchronní.

Pokyny pro autory knihoven

Tato část obsahuje pokyny pro autory knihoven, kteří vycházejí ze směrování. Cílem těchto podrobností je zajistit, aby vývojáři aplikací měli dobré zkušenosti s používáním knihoven a architektur, které rozšiřují směrování.

Definování koncových bodů

Pokud chcete vytvořit architekturu, která používá směrování pro porovnávání adres URL, začněte definováním uživatelského prostředí, které je postavené na UseEndpoints.

DO staví na vrcholu IEndpointRouteBuilder. To umožňuje uživatelům vytvářet architekturu s jinými funkcemi ASP.NET Core bez nejasností. Každá šablona ASP.NET Core zahrnuje směrování. Předpokládejme, že směrování je pro uživatele k dispozici a známé.

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...);

    endpoints.MapHealthChecks("/healthz");
});

DO vrátit zapečetěný beton typ z volání MapMyFramework(...) , které implementuje IEndpointConventionBuilder. Většina metod architektury Map... tento vzor dodržuje. Rozhraní IEndpointConventionBuilder :

  • Umožňuje kompozičnost metadat.
  • Cílí na různé rozšiřující metody.

Deklarování vlastního typu umožňuje do tvůrce přidat vlastní funkce specifické pro architekturu. Je v pořádku zabalit tvůrce deklarovaného architekturou a předat do něj volání.

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...).RequireAuthorization()
                                 .WithMyFrameworkFeature(awesome: true);

    endpoints.MapHealthChecks("/healthz");
});

ZVAŽTE psaní vlastního EndpointDataSource. EndpointDataSource je primitivní úroveň nízké úrovně pro deklarování a aktualizaci kolekce koncových bodů. EndpointDataSource je výkonné rozhraní API používané kontrolery a Razor stránkami.

Testy směrování mají základní příklad neaktualizuje zdroje dat.

VE výchozím nastavení se nepokoušejte EndpointDataSource o registraci. Vyžadovat, aby uživatelé zaregistrovali vaši architekturu v UseEndpoints. Filozofie směrování spočívá v tom, že ve výchozím nastavení se nic nezahrnuje a je to UseEndpoints místo pro registraci koncových bodů.

Vytvoření middlewaru integrovaného se směrováním

ZVAŽTE definování typů metadat jako rozhraní.

Umožňuje použít typy metadat jako atribut tříd a metod.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => true;
}

Architektury, jako jsou kontrolery a Razor stránky, podporují použití atributů metadat na typy a metody. Pokud deklarujete typy metadat:

  • Zpřístupní je jako atributy.
  • Většina uživatelů má zkušenosti s používáním atributů.

Deklarace typu metadat jako rozhraní přidává další vrstvu flexibility:

  • Rozhraní jsou kompozibilní.
  • Vývojáři můžou deklarovat své vlastní typy, které kombinují více zásad.

Umožňuje přepsat metadata, jak je znázorněno v následujícím příkladu:

public interface ICoolMetadata
{
    bool IsCool { get; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => true;
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SuppressCoolMetadataAttribute : Attribute, ICoolMetadata
{
    public bool IsCool => false;
}

[CoolMetadata]
public class MyController : Controller
{
    public void MyCool() { }

    [SuppressCoolMetadata]
    public void Uncool() { }
}

Nejlepší způsob, jak postupovat podle těchto pokynů, je vyhnout se definování metadat značek:

  • Nehledáte jenom přítomnost typu metadat.
  • Definujte vlastnost metadat a zkontrolujte vlastnost.

Kolekce metadat je seřazená a podporuje přepsání podle priority. V případě kontrolerů jsou metadata pro metodu akce nejvýraznější.

Do make middleware useful with and without routing.

app.UseRouting();

app.UseAuthorization(new AuthorizationPolicy() { ... });

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...).RequireAuthorization();
});

Jako příklad tohoto návodu UseAuthorization zvažte middleware. Middleware pro autorizaci umožňuje předávat záložní zásady. Záložní zásada, pokud je zadaná, platí pro obě:

  • Koncové body bez zadané zásady
  • Požadavky, které neodpovídají koncovému bodu.

Díky tomu je middleware autorizace užitečný mimo kontext směrování. Autorizační middleware lze použít pro tradiční programování middlewaru.

Ladění diagnostiky

Podrobný výstup diagnostiky směrování nastavíte Logging:LogLevel:Microsoft na Debughodnotu . Ve vývojovém prostředí nastavte úroveň protokolu v appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}