Dela via


Routning i ASP.NET Core

Av Ryan Nowak, Kirk Larkin och Rick Anderson

Note

Det här är inte den senaste versionen av den här artikeln. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Warning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Important

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Routning ansvarar för att matcha inkommande HTTP-begäranden och skicka dessa begäranden till appens körbara slutpunkter. Slutpunkter är appens enheter för körbar kod för hantering av begäranden. Slutpunkter definieras i appen och konfigureras när appen startas. Slutpunktsmatchningsprocessen kan extrahera värden från begärans URL och ange dessa värden för bearbetning av begäranden. Med hjälp av slutpunktsinformation från appen kan routning också generera URL:er som mappar till slutpunkter.

Appar kan konfigurera routning med hjälp av:

  • Controllers
  • Razor sidor
  • SignalR
  • gRPC-tjänster
  • Slutpunktsaktiverat mellanprogram , till exempel hälsokontroller.
  • Delegater och lambdas som registrerats med routning.

Den här artikeln beskriver information på låg nivå om ASP.NET Core-routning. Information om hur du konfigurerar routning:

Grunderna för routning

Följande kod visar ett grundläggande exempel på routning:

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

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

app.Run();

Föregående exempel innehåller en enskild slutpunkt med hjälp av MapGet metoden:

  • När en HTTP-begäran GET skickas till rot-URL:en /:
    • Begärandedelegaten utförs.
    • Hello World! skrivs till HTTP-svaret.
  • Om begärandemetoden inte är GET eller om rot-URL:en inte är /, matchar ingen rutt och en HTTP 404 returneras.

Routning använder ett par mellanprogram, registrerade av UseRouting och UseEndpoints:

  • UseRouting lägger till routningsmatchning till pipelinen för mellanprogram. Det här mellanprogrammet tittar på den uppsättning slutpunkter som definierats i appen och väljer den bästa matchningen baserat på begäran.
  • UseEndpoints lägger till slutpunktskörning i pipelinen för mellanprogram. Den kör delegeringen som är kopplad till den valda slutpunkten.

Appar behöver vanligtvis inte anropa UseRouting eller UseEndpoints. WebApplicationBuilder konfigurerar en middleware-pipeline som omsluter den middleware som lagts till i Program.cs med UseRouting och UseEndpoints. Appar kan dock ändra i vilken UseRouting ordning och UseEndpoints körs genom att anropa dessa metoder explicit. Följande kod gör till exempel ett explicit anrop till UseRouting:

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

app.UseRouting();

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

I koden ovan:

  • Anropet till app.Use registrerar ett anpassat mellanprogram som körs i början av pipelinen.
  • Anropet till UseRouting konfigurerar mellanprogrammet för ruttmatchning att köras efter det anpassade mellanprogrammet.
  • Slutpunkten som registrerats med MapGet körs i slutet av pipeline.

Om föregående exempel inte innehöll ett anrop till UseRoutingskulle det anpassade mellanprogrammet köras efter vägen som matchar mellanprogrammet.

Obs! Rutterna som läggs till direkt i WebApplication utförs vid slutet av pipelinen.

Endpoints

Metoden MapGet används för att definiera en slutpunkt. En slutpunkt är något som kan vara:

  • Vald genom att matcha URL:en och HTTP-metoden.
  • Utförs genom att köra delegeringen.

Ändpunkter som kan matchas och köras av appen konfigureras i UseEndpoints. Till exempel ansluter MapGet, MapPost och liknande metoder förbinder ombud för begäranden till routningssystemet. Ytterligare metoder kan användas för att ansluta ASP.NET Core Framework-funktioner till routningssystemet:

I följande exempel visas routning med en mer avancerad vägmall:

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

Strängen /hello/{name:alpha} är en vägmall. En vägmall används för att konfigurera hur slutpunkten matchas. I det här fallet matchar mallen:

  • En URL som /hello/Docs
  • Alla URL-sökvägar som börjar med /hello/ följt av en sekvens med alfabetiska tecken. :alpha tillämpar en vägbegränsning som endast matchar alfabetiska tecken. Routningsbegränsningar beskrivs senare i den här artikeln.

Det andra segmentet i URL-sökvägen: {name:alpha}

I följande exempel visas routning med hälsokontroller och auktorisering:

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

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

Föregående exempel visar hur:

  • Mellanprogrammet för auktorisering kan användas med routning.
  • Slutpunkter kan användas för att konfigurera auktoriseringsbeteende.

Anropet MapHealthChecks lägger till en slutpunkt för hälsokontroll. Länkning RequireAuthorization till det här anropet kopplar en auktoriseringsprincip till slutpunkten.

Anropar UseAuthentication och UseAuthorization lägger till mellanprogrammet för autentisering och auktorisering. Dessa mellanprogram placeras mellan UseRouting och UseEndpoints så att de kan:

  • Se vilken slutpunkt som har valts av UseRouting.
  • Tillämpa en auktoriseringsprincip innan UseEndpoints skickas till slutpunkten.

Slutpunktsmetadata

I föregående exempel finns det två slutpunkter, men endast slutpunkten för hälsokontroll har en auktoriseringsprincip kopplad. Om begäran matchar slutpunkten /healthzför hälsokontroll utförs en auktoriseringskontroll. Detta visar att slutpunkter kan ha extra data kopplade till sig. Dessa extra data kallas slutpunktsmetadata:

  • Metadata kan bearbetas av routningsmedvetna mellanprogram.
  • Metadata kan vara av valfri .NET-typ.

Routningsbegrepp

Routningssystemet bygger på pipelinen för mellanprogram genom att lägga till det kraftfulla slutpunktskonceptet . Slutpunkter representerar enheter i appens funktioner som skiljer sig från varandra när det gäller routning, auktorisering och valfritt antal ASP.NET Core-system.

ASP.NET Core-slutpunktsdefinition

En ASP.NET Core-slutpunkt är:

Följande kod visar hur du hämtar och inspekterar slutpunkten som matchar den aktuella begäran:

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

Slutpunkten, om den är markerad, kan hämtas från HttpContext. Dess egenskaper kan inspekteras. Slutpunktsobjekt är oföränderliga och kan inte ändras när de har skapats. Den vanligaste typen av slutpunkt är en RouteEndpoint. RouteEndpoint innehåller information som gör att den kan väljas av routningssystemet.

I föregående kod konfigurerar app.Use en inline-mellanprogram.

Följande kod visar att det, beroende på var app.Use som anropas i pipelinen, kanske inte finns någon slutpunkt:

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

Föregående exempel lägger till Console.WriteLine instruktioner som visar om en slutpunkt har valts eller inte. För tydlighetens skull tilldelar exemplet ett visningsnamn till den angivna / slutpunkten.

Föregående exempel innehåller även anrop till UseRouting och UseEndpoints för att styra exakt när dessa mellanprogram körs i pipelinen.

Kör den här koden med en URL på / visar:

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

Om du kör den här koden med andra URL:ar visas:

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

Dessa utdata visar att:

  • Slutpunkten är alltid null innan UseRouting anropas.
  • Om en matchning hittas är slutpunkten inte null mellan UseRouting och UseEndpoints.
  • Mellanprogrammet UseEndpoints är terminal när en matchning hittas. Terminalmellanprogram definieras senare i den här artikeln.
  • Mellanprogrammet efter UseEndpoints körs endast när ingen matchning hittas.

Mellanprogrammet UseRouting använder SetEndpoint metoden för att koppla slutpunkten till den aktuella kontexten. Det går att ersätta UseRouting mellanprogrammet med anpassad logik och ändå få fördelarna med att använda slutpunkter. Ändpunkter är ett grundläggande element på låg nivå som mellanvaror och är inte kopplade till routningens implementering. De flesta appar behöver inte ersättas UseRouting med anpassad logik.

Mellanprogrammet UseEndpoints är utformat för att användas tillsammans med UseRouting mellanprogrammet. Kärnlogiken för att utföra en slutpunkt är inte komplicerad. Använd GetEndpoint för att hämta slutpunkten och anropa sedan dess RequestDelegate egenskap.

Följande kod visar hur mellanprogram kan påverka eller reagera på routning:

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 { }

Föregående exempel visar två viktiga begrepp:

  • Mellanprogram kan köras tidigare UseRouting för att ändra de data som routningen körs på.
  • Mellanprogram kan köras mellan UseRouting och UseEndpoints för att bearbeta resultatet av routingen innan slutpunkten körs.
    • Mellanprogram som körs mellan UseRouting och UseEndpoints:
      • Inspekterar vanligtvis metadata för att förstå slutpunkterna.
      • Fattar ofta säkerhetsbeslut, som görs av UseAuthorization och UseCors.
    • Kombinationen av mellanprogram och metadata gör det möjligt att konfigurera principer per slutpunkt.

Föregående kod visar ett exempel på ett anpassat mellanprogram som stöder principer per slutpunkt. Mellanprogrammet skriver en revisionslogg av åtkomst av känsliga data till konsolen. Mellanprogrammet kan konfigureras för att granska en slutpunkt med RequiresAuditAttribute metadata. Det här exemplet visar ett opt-in-mönster där endast slutpunkter som är markerade som känsliga granskas. Det är möjligt att definiera den här logiken omvänt och granska allt som inte är markerat som säkert, till exempel. Slutpunktsmetadatasystemet är flexibelt. Den här logiken kan utformas på vilket sätt som helst som passar användningsfallet.

Föregående exempelkod är avsedd att demonstrera de grundläggande begreppen för slutpunkter. Exemplet är inte avsett för produktionsanvändning. En mer fullständig version av ett mellanprogram för granskningsloggar skulle:

  • Logga in på en fil eller databas.
  • Inkludera information som användaren, IP-adressen, namnet på den känsliga slutpunkten med mera.

Metadata för revisionspolicy RequiresAuditAttribute definieras som en Attribute för att enklare kunna använda klassbaserade ramverk som kontroller och SignalR. När du använder route to code:

  • Metadata bifogas med ett builder-API.
  • Klassbaserade ramverk innehåller alla attribut på motsvarande metod och klass när du skapar slutpunkter.

Metodtipsen för metadatatyper är att definiera dem antingen som gränssnitt eller attribut. Gränssnitt och attribut tillåter återanvändning av kod. Metadatasystemet är flexibelt och medför inga begränsningar.

Jämför terminalmellanprogram med routning

I följande exempel visas både terminalmellanprogram och routning:

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

Formatet för mellanprogram som visas med Approach 1: är terminalmellanprogram. Det kallas terminalmellanprogram eftersom det utför en matchande åtgärd:

  • Matchningsåtgärden i föregående exempel är Path == "/" för mellanprogrammet och Path == "/Routing" för routning.
  • När en matchning lyckas, utför den vissa funktioner och returnerar istället för att anropa next-mellanprogrammet.

Det kallas terminalmellanprogram eftersom det avslutar sökningen, kör vissa funktioner och sedan returnerar.

I följande lista jämförs terminalmellanprogram med routning:

  • Båda metoderna gör det möjligt att avsluta bearbetningspipelinen:
    • Mellanprogram avslutar pipelinen genom att returnera i stället för att nextanropa .
    • Slutpunkter är alltid terminaler.
  • Med terminalmellanprogram kan du placera mellanprogrammet på en godtycklig plats i pipelinen:
  • Med terminalmellanprogram kan godtycklig kod avgöra när mellanprogrammet matchar:
    • Anpassad vägmatchningskod kan vara utförlig och svår att skriva korrekt.
    • Routning ger enkla lösningar för typiska appar. De flesta appar kräver inte anpassad vägmatchningskod.
  • Slutpunktsgränssnitt med mellanprogram som UseAuthorization och UseCors.
    • Användning av ett terminalmellanprogram med UseAuthorization eller UseCors kräver manuell koppling med auktoriseringssystemet.

En slutpunkt definierar båda:

  • Ett ombud för att bearbeta begäranden.
  • En samling godtyckliga metadata. Metadata används för att implementera övergripande problem baserat på principer och konfiguration som är kopplade till varje slutpunkt.

Terminalmellanprogram kan vara ett effektivt verktyg, men kan kräva:

  • En betydande mängd kodning och testning.
  • Manuell integrering med andra system för att uppnå önskad flexibilitetsnivå.

Överväg att integrera med routning innan du skriver ett terminalmellanprogram.

Befintliga terminalmellanprogram som integreras med Map eller MapWhen kan vanligtvis omvandlas till en routingmedveten slutpunkt. MapHealthChecks visar mönstret för router-ware:

  • Skriv en tilläggsmetod på IEndpointRouteBuilder.
  • Skapa en pipeline för kapslade mellanprogram med CreateApplicationBuilder.
  • Koppla mellanprogrammet till den nya pipelinen. I det här fallet UseHealthChecks.
  • Build mellanprogramspipelinen till en RequestDelegate.
  • Anropa Map och ange den nya pipelinen för mellanprogram.
  • Returnera builder-objektet som tillhandahålls av Map från tilläggsmetoden.

Följande kod visar användning av MapHealthChecks:

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

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

Föregående exempel visar varför det är viktigt att returnera builder-objektet. När byggobjektet returneras kan apputvecklaren konfigurera principer som auktorisering för slutpunkten. I det här exemplet har mellanprogrammet för hälsokontroller ingen direkt integrering med auktoriseringssystemet.

Metadatasystemet skapades som svar på de problem som uppstått av utökningsförfattare med hjälp av terminalmellanprogram. Det är problematiskt för varje mellanprogram att implementera sin egen integrering med auktoriseringssystemet.

URL-matchning

  • Är den process genom vilken routningen matchar en inkommande begäran till en slutpunkt.
  • Baseras på data i URL-sökvägen och rubrikerna.
  • Kan utökas för att överväga alla data i begäran.

När ett routningsmellanprogram körs anger det en Endpoint och dirigerar värden till en begäransfunktionHttpContext från den aktuella begäran:

  • Genom att anropa HttpContext.GetEndpoint hämtas slutpunkten.
  • HttpRequest.RouteValues hämtar samlingen av routvärden.

Mellanprogram som körs efter routningsmellanprogrammet kan inspektera slutpunkten och vidta åtgärder. Till exempel kan ett mellanprogramvara för auktorisering granska och utvärdera slutpunktens metadatasamling för en auktoriseringspolicy. När alla mellanprogram i pipelinen för bearbetning av begäranden har körts anropas den valda slutpunktens ombud.

Routningssystemet i slutpunktsroutningen ansvarar för alla leveransbeslut. Eftersom mellanprogrammet tillämpar principer baserat på den valda slutpunkten är det viktigt att:

  • Alla beslut som kan påverka sändningen eller tillämpningen av säkerhetsprinciper fattas i routningssystemet.

Warning

För bakåtkompatibilitet, när en Controller eller Razor Pages-slutpunktsdelegat körs, anges egenskaperna till lämpliga värden baserat på den bearbetning som hittills utförts av RouteContext.RouteData-begäran.

Typen RouteContext kommer att markeras som föråldrad i en framtida version:

  • Migrera RouteData.Values till HttpRequest.RouteValues.
  • Migrera RouteData.DataTokens för att hämta IDataTokensMetadata från slutpunktsmetadata.

URL-matchning fungerar i en konfigurerbar uppsättning faser. I varje fas är utdata en uppsättning matchningar. Mängden matcher kan begränsas ytterligare under nästa fas. Routningsimplementeringen garanterar inte någon bearbetningsordning för matchande slutpunkter. Alla möjliga matchningar bearbetas samtidigt. Url-matchningsfaserna sker i följande ordning. ASP.NET Core:

  1. Bearbetar URL-sökvägen mot uppsättningen slutpunkter och deras vägmallar och samlar in alla matchningar.
  2. Tar den föregående listan och tar bort matchningar som inte uppfyller ruttbegränsningar som tillämpas.
  3. Tar föregående lista och tar bort matchningar som inte uppfyller kraven för MatcherPolicy instanser.
  4. Använder EndpointSelector för att fatta ett slutgiltigt beslut från föregående lista.

Listan över slutpunkter prioriteras enligt:

Alla matchande ändpunkter bearbetas i varje fas tills den EndpointSelector har nåtts. EndpointSelector är den sista fasen. Den väljer den högsta prioritetsslutpunkten från matchningarna som den bästa matchningen. Om det finns andra matchningar med samma prioritet som den bästa matchningen, kommer ett tvetydigt matchningsundantag att utlösas.

Routningsprioriteten beräknas baserat på att en mer specifik vägmall får högre prioritet. Tänk till exempel på mallarna /hello och /{message}:

  • Båda matchar URL-sökvägen /hello.
  • /hello är mer specifik och därför högre prioritet.

I allmänhet gör routningsprioriteten ett bra jobb med att välja den bästa matchningen för de typer av URL-scheman som används i praktiken. Använd Order endast när det behövs för att undvika tvetydigheter.

På grund av de typer av utökningsbarhet som tillhandahålls av routning är det inte möjligt för routningssystemet att beräkna de tvetydiga vägarna i förväg. Överväg ett exempel som routningsmallarna /{message:alpha} och /{message:int}:

  • Villkoret alpha matchar endast alfabetiska tecken.
  • Villkoret int matchar endast tal.
  • Dessa mallar har samma vägprioritet, men det finns ingen enskild URL som båda matchar.
  • Om routningssystemet rapporterade ett tvetydighetsfel vid starten skulle det blockera det här giltiga användningsfallet.

Warning

Ordningen på åtgärderna i UseEndpoints påverkar inte beteendet för routning, med ett undantag. MapControllerRoute och MapAreaRoute tilldela automatiskt ett ordervärde till sina slutpunkter baserat på den ordning som de anropas. Detta simulerar långvarigt beteende för kontrollanter utan att routningssystemet ger samma garantier som äldre routningsimplementeringar.

Slutpunktsroutning i ASP.NET Core:

  • Har inte begreppet rutter.
  • Ger inte ordergarantier. Alla slutpunkter bearbetas samtidigt.

Routningsmallens prioritet och slutpunktsmarkeringsordning

Prioritet för routningsmallar är ett system som tilldelar varje vägmall ett värde baserat på hur specifik den är. Prioritet för routningsmall:

  • Undviker behovet av att justera ordningen på slutpunkter i vanliga fall.
  • Försöker matcha allmänna förväntningar på routingbeteende.

Du kan till exempel överväga mallar /Products/List och /Products/{id}. Det är rimligt att anta att det /Products/List är en bättre matchning än /Products/{id} för URL-sökvägen /Products/List. Detta fungerar eftersom literalsegmentet /List anses ha bättre prioritet än parametersegmentet /{id}.

Information om hur prioritet fungerar är kopplat till hur routningsmallar definieras:

  • Mallar med fler segment anses vara mer specifika.
  • Ett segment med literaltext anses vara mer specifikt än ett parametersegment.
  • Ett parametersegment med en begränsning anses vara mer specifikt än ett utan.
  • Ett komplext segment anses vara lika specifikt som ett parametersegment med en begränsning.
  • Catch-all-parametrar är de minst specifika. Se catch-all i avsnittet Routningsmallar för viktig information om catch-all-vägar.

Begrepp för URL-generering

URL-generering:

  • Är den process genom vilken routning kan skapa en URL-sökväg baserat på en uppsättning vägvärden.
  • Tillåter en logisk separation mellan slutpunkter och URL:er som har åtkomst till dem.

Slutpunktsroutning innehåller API:et LinkGenerator . LinkGenerator är en singleton-tjänst som är tillgänglig från DI. API LinkGenerator kan användas utanför kontexten för en exekverande begäran. Mvc.IUrlHelper och scenarier som förlitar sig på IUrlHelper, till exempel Tag Helpers, HTML Helpers och Action Results, använder API:et LinkGenerator internt för att tillhandahålla funktioner för länkgenerering.

Länkgeneratorn backas upp av begreppet adress - och adressscheman. Ett adressschema är ett sätt att fastställa de slutpunkter som ska beaktas för länkgenerering. Till exempel implementeras scenarier med routningnamn och routvärden som många användare är bekanta med från kontroller och Razor sidor såsom ett adressschema.

Länkgeneratorn kan länka till styrenheter och Razor sidor via följande tilläggsmetoder:

Överbelastningar av dessa metoder tar emot argument som inkluderar HttpContext. Dessa metoder är funktionellt likvärdiga med Url.Action och Url.Page, men ger ytterligare flexibilitet och alternativ.

Metoderna GetPath* liknar Url.Action mest och Url.Page, eftersom de genererar en URI som innehåller en absolut sökväg. Metoderna GetUri* genererar alltid en absolut URI som innehåller ett schema och en värd. De metoder som accepterar en HttpContext genererar en URI i kontexten för den körande begäran. Värden ambienta för väg, URL-bassökväg, protokoll och värd från den körande begäran används om de inte åsidosätts.

LinkGenerator anropas med en adress. Generera en URI sker i två steg:

  1. En adress är bunden till en lista över slutpunkter som matchar adressen.
  2. Varje slutpunkts RoutePattern utvärderas tills ett vägmönster som matchar de angivna värdena hittas. Resultatet kombineras med de andra URI-delarna som levereras till länkgeneratorn och returneras.

Metoderna som tillhandahålls av LinkGenerator stöder standardfunktioner för länkgenerering för alla typer av adresser. Det enklaste sättet att använda länkgeneratorn är genom tilläggsmetoder som utför åtgärder för en viss adresstyp:

Tilläggsmetod Description
GetPathByAddress Genererar en URI med en absolut sökväg baserat på de angivna värdena.
GetUriByAddress Genererar en absolut URI baserat på de angivna värdena.

Warning

Var uppmärksam på följande konsekvenser av att anropa LinkGenerator metoder:

  • Använd GetUri* tilläggsmetoder med försiktighet i en appkonfiguration som inte validerar headern för inkommande begäranden. Host Om rubriken för inkommande begäranden inte verifieras kan ej betrodda begärandeindata skickas tillbaka till klienten i URI:er i en vy eller sida. Vi rekommenderar att alla produktionsappar konfigurerar sin server för att kontrollera Host headern mot kända giltiga värden.

  • Använd LinkGenerator med försiktighet i mellanprogram i kombination med Map eller MapWhen. Map* ändrar basvägen för den körande begäran, vilket påverkar utdata från länkgenereringen. LinkGenerator Alla API:er tillåter att du anger en bassökväg. Ange en tom bassökväg för att ta bort Map* effekten på länkgenereringen.

Exempel på mellanprogram

I följande exempel använder ett mellanprogram API:et LinkGenerator för att skapa en länk till en åtgärdsmetod som visar lagerprodukter. Använda länkgeneratorn genom att mata in den i en klass och anrop GenerateLink är tillgängligt för alla klasser i en app:

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

Routningsmallar

Token inom {} definierar ruttparametrar som är bundna om rutten matchas. Mer än en vägparameter kan definieras i ett vägsegment, men vägparametrarna måste avgränsas med ett literalvärde. Till exempel:

{controller=Home}{action=Index}

är inte en giltig väg eftersom det inte finns något literalvärde mellan {controller} och {action}. Routningsparametrarna måste ha ett namn och kan ha ytterligare attribut angivna.

Annan literaltext än vägparametrar (till exempel {id}) och sökvägsavgränsaren / måste matcha texten i URL:en. Textmatchning är skiftlägesokänslig och baseras på den avkodade representationen av URL:ens sökväg. Om du vill matcha en literalvägsparameter avgränsare { eller }kan du undvika avgränsare genom att upprepa tecknet. Till exempel {{ eller }}.

Asterisk * eller dubbel asterisk **:

  • Kan användas som ett prefix till en vägparameter för att binda till resten av URI:n.
  • Kallas för catch-all-parametrar. Till exempel blog/{**slug}:
    • Matchar alla URI:er som börjar med blog/ och har ett värde som följer den.
    • Värdet som följer efter blog/ tilldelas som värde för slug-routen.

Warning

En parameter kan matcha vägar felaktigt på grund av en bugg i routning. Appar som påverkas av den här buggen har följande egenskaper:

  • En allomfattande rutt, till exempel {**slug}"
  • Catch-all-vägen matchar inte begäranden som den ska matcha.
  • Om du tar bort andra rutter börjar den allomfattande rutten att fungera.

Se GitHub-buggar 18677 och 16579 till exempel fall som drabbat den här buggen.

En anmälningskorrigering för den här buggen finns i .NET Core 3.1.301 eller senare SDK. Följande kod anger en intern växel som åtgärdar felet:

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

Catch-all-parametrar kan även matcha den tomma strängen.

Parametern catch-all undflyr lämpliga tecken när vägen används för att generera en URL, inklusive sökvägsavgränsningstecken / . Till exempel genererar rutten foo/{*path} med ruttvärden { path = "my/path" }foo/my%2Fpath. Observera det undantagna snedstrecket. Använd routningsparameterprefixet ** för att avgränsa sökvägens avgränsningstecken. Vägen foo/{**path} med { path = "my/path" } genererar foo/my/path.

URL-mönster som försöker avbilda ett filnamn med ett valfritt filnamnstillägg har ytterligare överväganden. Överväg till exempel mallen files/{filename}.{ext?}. När värden för båda filename och ext finns fylls båda värdena i. Om det bara finns ett värde för filename i URL:en matchar vägen eftersom avslutandet . är valfritt. Följande URL:er matchar den här vägen:

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

Routningsparametrar kan ha standardvärden som anges genom att ange standardvärdet efter parameternamnet avgränsat med ett likhetstecken (=). Definierar {controller=Home}Home till exempel som standardvärde för controller. Standardvärdet används om inget värde finns i URL:en för parametern. Routningsparametrar görs valfria genom att ett frågetecken (?) läggs till i slutet av parameternamnet. Till exempel id?. Skillnaden mellan valfria värden och standardvägsparametrar är:

  • En routningsparameter med ett standardvärde genererar alltid ett värde.
  • En valfri parameter har endast ett värde när ett värde tillhandahålls av begärande-URL:en.

Ruttparametrarna kan ha begränsningar som måste matcha ruttvärdet som är bundet från URL:en. Att lägga till : och villkorsnamn efter namnet på routningsparametern anger en infogad begränsning för en routningsparameter. Om villkoret kräver argument omges de av parenteser (...) efter villkorsnamnet. Du kan ange flera inline-begränsningar genom att lägga till en annan : och ett annat begränsningsnamn.

Villkorsnamnet och argumenten IInlineConstraintResolver skickas till tjänsten för att skapa en instans av IRouteConstraint som ska användas i URL-bearbetning. Routningsmallen blog/{article:minlength(10)} anger till exempel en minlength begränsning med argumentet 10. Mer information om routningsbegränsningar och en lista över de begränsningar som tillhandahålls av ramverket finns i avsnittet Routningsbegränsningar .

Routningsparametrar kan också ha parametertransformatorer. Parametertransformatorer transformerar en parameters värde vid generering av länkar och matchande åtgärder och sidor till URL:er. Precis som begränsningar kan parametertransformatorer läggas till direkt i en routparameter genom att lägga till ett : och ett transformeringsnamn efter routparameternamnet. Routningsmallen blog/{article:slugify} anger till exempel en slugify transformerare. Mer information om parametertransformatorer finns i avsnittet Parametertransformatorer .

I följande tabell visas exempel på routningsmallar och deras beteende:

Routningsmall Exempel på matchande URI Förfrågan-URI:n...
hello /hello Matchar endast den ensamma sökvägen /hello.
{Page=Home} / Matchar och anger Page till Home.
{Page=Home} /Contact Matchar och anger Page till Contact.
{controller}/{action}/{id?} /Products/List Mappar till kontroller Products och åtgärd List.
{controller}/{action}/{id?} /Products/Details/123 Mappar till kontrollanten Products och Details åtgärden medid värdet 123.
{controller=Home}/{action=Index}/{id?} / Mappar till Home-kontrollern och Index-metoden. id ignoreras.
{controller=Home}/{action=Index}/{id?} /Products Mappar till Products-kontrollern och Index-metoden. id ignoreras.

Att använda en mall är vanligtvis den enklaste metoden för routning. Begränsningar och standardvärden kan också anges utanför vägmallen.

Komplexa segment

Komplexa segment bearbetas genom matchning av literalavgränsare från höger till vänster på ett icke-girigt sätt. Till exempel [Route("/a{b}c{d}")] är ett komplext segment. Komplexa segment fungerar på ett visst sätt som måste tolkas för att kunna använda dem. Exemplet i det här avsnittet visar varför komplexa segment bara fungerar bra när avgränsartexten inte visas i parametervärdena. Att använda en regex och sedan extrahera värdena manuellt behövs för mer komplexa fall.

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Det här är en sammanfattning av de steg som routningen utför med mallen /a{b}c{d} och URL-sökvägen /abcd. | Används för att visualisera hur algoritmen fungerar:

  • Den första literalen, från höger till vänster, är c. Så /abcd söks från höger och hittar /ab|c|d.
  • Allt till höger (d) matchas nu med routningsparametern {d}.
  • Nästa literal, från höger till vänster, är a. Så /ab|c|d genomsöks från där vi slutade, sedan hittas a/|a|b|c|d.
  • Värdet till höger (b) matchas nu med routningsparametern {b}.
  • Det finns ingen kvarvarande text och ingen kvarvarande mall för rutt, så det här är en matchning.

Här är ett exempel på ett negativt ärende med samma mall /a{b}c{d} och URL-sökvägen /aabcd. | Används för att visualisera hur algoritmen fungerar. Det här fallet är inte en matchning, vilket förklaras av samma algoritm:

  • Den första literalen, från höger till vänster, är c. Så /aabcd söks från höger och hittar /aab|c|d.
  • Allt till höger (d) matchas nu med routningsparametern {d}.
  • Nästa literal, från höger till vänster, är a. Så /aab|c|d genomsöks från där vi slutade, sedan hittas a/a|a|b|c|d.
  • Värdet till höger (b) matchas nu med routningsparametern {b}.
  • I det här läget finns det fortfarande text a, men algoritmen har slut på vägmall för analys, så detta stämmer inte.

Eftersom matchningsalgoritmen inte är girig:

  • Den matchar den minsta möjliga mängden text i varje steg.
  • Alla fall där avgränsarvärdet visas i parametervärdena resulterar i att det inte matchar.

Reguljära uttryck ger mycket mer kontroll över deras matchande beteende.

Girig matchning, även kallat maximal matchning , försöker hitta den längsta möjliga matchningen i indatatexten som uppfyller regex-mönstret . Icke-grådig matchning, även kallad försiktig matchning, söker den kortaste möjliga matchningen i inmatningstexten som uppfyller regex-uttrycket.

Routning med specialtecken

Routning med specialtecken kan leda till oväntade resultat. Tänk dig till exempel en kontrollant med följande åtgärdsmetod:

[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;
}

När string id innehåller följande kodade värden kan oväntade resultat inträffa:

ASCII Encoded
/ %2F
+

Routningsparametrar är inte alltid URL-avkodade. Det här problemet kan åtgärdas i framtiden. Mer information finns i det här GitHub-problemet;

Vägbegränsningar

Routningsbegränsningar körs när en matchning har hittats för den inkommande URL:en och URL-sökvägen tokeniseras till ruttvärden. Routningsbegränsningar inspekterar vanligtvis det vägvärde som är associerat via routningsmallen och fattar ett sant eller falskt beslut om huruvida värdet är acceptabelt. Vissa routningsbegränsningar använder data utanför vägvärdet för att överväga om begäran kan dirigeras. Till exempel HttpMethodRouteConstraint kan acceptera eller avvisa en begäran baserat på dess HTTP-verb. Begränsningar används i routningsbegäranden och länkgenerering.

Warning

Använd inte begränsningar för indataverifiering. Om begränsningar används för indataverifiering resulterar ogiltiga indata i ett 404 svar som inte hittades. Ogiltiga indata bör generera en 400 felaktig begäran med ett lämpligt felmeddelande. Routningsbegränsningar används för att särskilja liknande rutter, inte för att verifiera inmatningar för en viss rutt.

I följande tabell visas standardvägsbegränsningarna och deras förväntade beteende:

constraint Example Exempelmatchningar Notes
int {id:int} 123456789, -123456789 Matchar alla heltal
bool {active:bool} true, FALSE Matchar true eller false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Matchar ett giltigt DateTime värde i den invarianta kulturen. Se föregående varning
decimal {price:decimal} 49.99, -1,000.01 Matchar ett giltigt decimal värde i den invarianta kulturen. Se föregående varning
double {weight:double} 1.234, -1,001.01e8 Matchar ett giltigt double värde i den invarianta kulturen. Se föregående varning
float {weight:float} 1.234, -1,001.01e8 Matchar ett giltigt float värde i den invarianta kulturen. Se föregående varning
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Matchar ett giltigt Guid värde
long {ticks:long} 123456789, -123456789 Matchar ett giltigt long värde
minlength(value) {username:minlength(4)} Rick Strängen måste innehålla minst 4 tecken
maxlength(value) {filename:maxlength(8)} MyFile Strängen får inte innehålla fler än 8 tecken
length(length) {filename:length(12)} somefile.txt Strängen måste vara exakt 12 tecken lång
length(min,max) {filename:length(8,16)} somefile.txt Strängen får vara minst 8 och högst 16 tecken lång
min(value) {age:min(18)} 19 Heltalsvärdet måste vara minst 18
max(value) {age:max(120)} 91 Heltalsvärdet får inte vara mer än 120
range(min,max) {age:range(18,120)} 91 Heltalsvärdet måste vara minst 18 men högst 120
alpha {name:alpha} Rick Strängen måste bestå av ett eller flera alfabetiska tecken a-z och skiftlägesokänslig
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 Strängen måste matcha det reguljära uttrycket. Se tips om hur du definierar ett reguljärt uttryck
required {name:required} Rick Används för att framtvinga att ett icke-parametervärde finns under URL-generering
file {filename:file} myfile.txt Strängen kan innehålla sökvägssegment, men dess sista segment måste ha en punkt (.) och följas av ett eller flera tecken som inte är punkttecken
nonfile {page:nonfile} PageName Strängen får inte ha en punkt i det sista sökvägssegmentet som följs av ett eller flera icke-punktstecken (.)

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Flera, kolonavgränsade begränsningar kan tillämpas på en enskild parameter. Följande villkor begränsar till exempel en parameter till ett heltalsvärde på 1 eller högre:

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

Warning

Routningsbegränsningar som verifierar URL:en och konverteras till en CLR-typ använder alltid den invarianta kulturen. Till exempel konvertering till CLR-typen int eller DateTime. Dessa begränsningar förutsätter att URL:en inte kan lokaliseras. De ramverksbaserade vägbegränsningarna ändrar inte de värden som lagras i vägvärden. Alla vägvärden som parsas från URL:en lagras som strängar. Villkoret float försöker till exempel konvertera vägvärdet till en flyttal, men det konverterade värdet används bara för att verifiera att det kan konverteras till en flyttal.

Reguljära uttryck i begränsningar

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Reguljära uttryck kan anges som infogade begränsningar med hjälp av routningsbegränsningen regex(...) . Metoder i MapControllerRoute familjen accepterar också en objektliteral av begränsningar. Om formuläret används tolkas strängvärden som reguljära uttryck.

Följande kod använder en infogad regex-begränsning:

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

Följande kod använder en objektliteral för att ange en regex-begränsning:

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

Ramverket ASP.NET Core lägger RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant till konstruktorn för reguljära uttryck. Se RegexOptions en beskrivning av dessa medlemmar.

Reguljära uttryck använder avgränsare och token som liknar dem som används vid routning och C#-språket. Token för reguljära uttryck måste vara undantagna. Om du vill använda reguljära uttryck ^\d{3}-\d{2}-\d{4}$ i en infogad begränsning använder du något av följande:

  • Ersätt \ tecken som anges i strängen som \\ tecken i C#-källfilen för att undvika strängens \ escape-tecken.
  • Verbatim strängliteraler.

Om du vill undvika dirigeringsparametertecken {, }, [, ], dubblar du tecknen i uttrycket, {{till exempel , }}, [[, . ]] I följande tabell visas ett reguljärt uttryck och dess undantagna version:

Reguljärt uttryck Undantagna reguljära uttryck
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Reguljära uttryck som används i routning börjar ofta med ^ tecknet och matchar strängens startposition. Uttrycken slutar ofta med $ tecknet och matchar slutet av strängen. Tecknen ^ och $ ser till att det reguljära uttrycket matchar värdet för hela routningsparametern. Utan ^- och $-tecknen matchar det reguljära uttrycket vilken delsträng som helst i strängen, vilket ofta är oönskat. Följande tabell innehåller exempel och förklarar varför de matchar eller inte matchar:

Expression String Match Comment
[a-z]{2} hello Yes Matchningar för delsträngar
[a-z]{2} 123abc456 Yes Matchningar för delsträngar
[a-z]{2} mz Yes Matchar uttryck
[a-z]{2} MZ Yes Inte skiftlägeskänslig
^[a-z]{2}$ hello No Se ^ och $ ovan
^[a-z]{2}$ 123abc456 No Se ^ och $ ovan

Mer information om syntax för reguljära uttryck finns i Reguljära .NET Framework-uttryck.

Om du vill begränsa en parameter till en känd uppsättning möjliga värden använder du ett reguljärt uttryck. Till exempel {action:regex(^(list|get|create)$)} matchar endast routningsvärdet action till list, geteller create. Om den skickas in i begränsningsordboken, så är strängen ^(list|get|create)$ likvärdig. Begränsningar som skickas i villkorsordlistan som inte matchar någon av de kända begränsningarna behandlas också som reguljära uttryck. Begränsningar som skickas i en mall som inte matchar någon av de kända begränsningarna behandlas inte som reguljära uttryck.

Begränsningar för anpassad rutt

Anpassade routningsbegränsningar kan skapas genom att implementera IRouteConstraint gränssnittet. Gränssnittet IRouteConstraint innehåller Match, som returnerar true om villkoret är uppfyllt och false på annat sätt.

Anpassade routningsbegränsningar behövs sällan. Innan du implementerar en anpassad vägbegränsning bör du överväga alternativ, till exempel modellbindning.

Mappen ASP.NET Core Constraints innehåller bra exempel på hur du skapar begränsningar. Till exempel GuidRouteConstraint.

Om du vill använda en anpassad IRouteConstraintmåste routningsbegränsningstypen registreras med appens ConstraintMap i tjänstcontainern. A ConstraintMap är en ordlista som mappar routningsbegränsningsnycklar till IRouteConstraint implementeringar som validerar dessa begränsningar. En app"s ConstraintMap kan uppdateras i Program.cs antingen som en del av ett AddRouting-anrop eller genom att konfigurera RouteOptions direkt med builder.Services.Configure<RouteOptions>. Till exempel:

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

Föregående villkor tillämpas i följande kod:

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

Implementeringen av NoZeroesRouteConstraint förhindrar 0 att den används i en vägparameter:

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

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Föregående kod:

  • Förhindrar 0 i {id} segmentet av rutten.
  • Visas som ett grundläggande exempel på hur du implementerar en anpassad begränsning. Den bör inte användas i en produktionsapp.

Följande kod är en bättre metod för att förhindra att en id innehållande en 0 bearbetas:

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

    return Content(id);
}

Föregående kod har följande fördelar jämfört med NoZeroesRouteConstraint metoden:

  • Det kräver ingen anpassad begränsning.
  • Det returnerar ett mer beskrivande fel när vägparametern innehåller 0.

Parametertransformatorer

Parametertransformatorer:

Till exempel, en anpassad slugify-parametertransformator i vägmönster blog\{article:slugify} med Url.Action(new { article = "MyTestArticle" }) genererar blog\my-test-article.

Överväg följande IOutboundParameterTransformer implementering:

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

Om du vill använda en parametertransformator i ett vägmönster konfigurerar du den med hjälp av ConstraintMap i Program.cs:

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

ASP.NET Core-ramverket använder parametertransformatorer för att transformera den URI där en slutpunkt löser problemet. Till exempel transformerar parametertransformatorer de vägvärden som används för att matcha en area, controller, actionoch page:

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

Med föregående vägmall matchas åtgärden SubscriptionManagementController.GetAll med URI /subscription-management/get-all:n . En parametertransformator ändrar inte de vägvärden som används för att generera en länk. Till exempel Url.Action("GetAll", "SubscriptionManagement") ger /subscription-management/get-all.

ASP.NET Core tillhandahåller API-konventioner för användning av parametertransformatorer med genererade vägar:

Referens för URL-generering

Det här avsnittet innehåller en referens för algoritmen som implementeras av URL-generering. I praktiken använder de flesta komplexa exempel på URL-generering styrenheter eller Razor sidor. Mer information finns i routning i kontrollanter .

URL-genereringsprocessen börjar med ett anrop till LinkGenerator.GetPathByAddress eller en liknande metod. Metoden tillhandahålls med en adress, en uppsättning vägvärden och eventuellt information om den aktuella begäran från HttpContext.

Det första steget är att använda adressen för att lösa en uppsättning kandidatslutpunkter med hjälp av en IEndpointAddressScheme<TAddress> som matchar adressens typ.

När uppsättningen kandidater hittas av adressschemat sorteras slutpunkterna och bearbetas iterativt tills en URL-genereringsåtgärd lyckas. URL-genereringen söker inte efter tvetydigheter. Det första resultatet som returneras är slutresultatet.

Felsöka URL-generering med loggning

Det första steget i felsökning av URL-generering för Microsoft.AspNetCore.Routing är att ställa in loggningsnivån till TRACE. LinkGenerator loggar många detaljer om bearbetningen som kan vara användbar för att felsöka problem.

Mer information om URL-generering finns i REFERENS för URL-generering .

Addresses

Adresser är det begrepp i URL-generering som används för att binda ett anrop till länkgeneratorn till en uppsättning kandidatslutpunkter.

Adresser är ett utökningsbart begrepp som levereras med två implementeringar som standard:

  • Använda slutpunktsnamnet (string) som adress:
    • Tillhandahåller liknande funktioner som MVC:s routningsnamn.
    • IEndpointNameMetadata Använder metadatatypen.
    • Löser den angivna strängen mot metadata för alla registrerade slutpunkter.
    • Utlöser ett undantag vid start om flera slutpunkter använder samma namn.
    • Rekommenderas för generell användning utanför kontrollanter och Razor sidor.
  • Använda vägvärden (RouteValuesAddress) som adress:
    • Ger liknande funktioner som kontroller och äldre URL-generering för sidor.
    • Mycket komplext att utöka och felsöka.
    • Tillhandahåller implementeringen som används av IUrlHelper, Tag Helpers, HTML-hjälpare, åtgärdsresultat osv.

Adressschemats roll är att göra associationen mellan adressen och matchande slutpunkter enligt godtyckliga kriterier:

  • Slutpunktsnamnschemat utför en grundläggande ordlistesökning.
  • Routningsvärdena har en komplex bästa delmängd av set-algoritmen.

Omgivande värden och explicita värden

Från den aktuella begäran får routning åtkomst till routningsvärdena för den aktuella begäran HttpContext.Request.RouteValues. De värden som är associerade med den aktuella begäran kallas för omgivande värden. För tydlighetens skull refererar dokumentationen till de vägvärden som skickas in till metoder som explicita värden.

I följande exempel visas omgivande värden och explicita värden. Den innehåller omgivande värden från den aktuella begäran och explicita värden:

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

    // ...

Föregående kod:

Följande kod innehåller endast explicita värden och inga omgivande värden:

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

Föregående metod returnerar /Home/Subscribe/17

Följande kod i WidgetController returnerar /Widget/Subscribe/17:

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

Följande kod tillhandahåller kontrollern från omgivande och explicita värden i den aktuella begäran.

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

I koden ovan:

  • /Gadget/Edit/17 returneras.
  • Url hämtar IUrlHelper.
  • Action genererar en URL med en absolut sökväg för en åtgärdsmetod. URL:en innehåller det angivna action namnet och route värdena.

Följande kod innehåller omgivande värden från den aktuella begäran och explicita värden:

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

        // ...
    }
}

Den föregående koden sätter url till /Edit/17 när Redigera Razor Sida innehåller följande siddirektiv:

@page "{id:int}"

Om sidan Redigera inte innehåller "{id:int}"-routemallen, är url/Edit?id=17.

Beteendet för MVC lägger IUrlHelper till ett lager av komplexitet utöver de regler som beskrivs här:

  • IUrlHelper tillhandahåller alltid vägvärdena från den aktuella begäran som omgivande värden.
  • IUrlHelper.Action kopierar alltid aktuella action värden och controller vägvärden som explicita värden om de inte åsidosätts av utvecklaren.
  • IUrlHelper.Page kopierar alltid det aktuella page vägvärdet som ett explicit värde om det inte åsidosätts.
  • IUrlHelper.Page åsidosätter alltid det aktuella handler ruttvärdet med null som ett explicit värde såvida det inte åsidosätts.

Användarna blir ofta förvånade över beteendeinformationen för omgivande värden, eftersom MVC inte verkar följa sina egna regler. Av historiska och kompatibilitetsskäl har vissa routningsvärden som action, controller, pageoch handler ett eget specialfallsbeteende.

Motsvarande funktionalitet som tillhandahålls av LinkGenerator.GetPathByAction och LinkGenerator.GetPathByPage duplicerar dessa avvikelser i IUrlHelper för kompatibilitet.

Process för URL-generering

När uppsättningen kandidatslutpunkter hittas, algoritmen för URL-generering:

  • Bearbetar slutpunkterna iterativt.
  • Returnerar det första lyckade resultatet.

Det första steget i den här processen kallas ogiltigförklarande av routningsvärden. Ogiltigförklaring av routningsvärden är den process genom vilken routning avgör vilka routningsvärden från bakgrundsvärden som ska användas och vilka som ska ignoreras. Varje omgivande värde beaktas och kombineras antingen med explicita värden eller ignoreras.

Det bästa sättet att förstå rollen av omgivande värden är att de försöker spara skrivande för programutvecklare i vissa vanliga situationer. Traditionellt är scenarier där omgivande värden är användbara relaterade till MVC:

  • När du länkar till en annan åtgärd i samma kontrollant behöver inte kontrollantnamnet anges.
  • När du länkar till en annan kontrollant i samma område behöver du inte ange områdesnamnet.
  • När du länkar till samma åtgärdsmetod behöver du inte ange vägvärden.
  • När du länkar till en annan del av appen vill du inte överföra routningsvärden som inte har någon betydelse i den delen av appen.

Anrop till LinkGenerator eller IUrlHelper som returnerar null orsakas vanligtvis av bristande förståelse av ogiltigförklaring av routningsvärden. Felsöka routningsvärdets ogiltighet genom att uttryckligen ange fler av vägvärdena för att se om det löser problemet.

Ogiltigförklaring av routvärden bygger på antagandet att appens URL-schema är hierarkiskt, med en hierarki som bildas från vänster till höger. Överväg den grundläggande mallen för styrenhetsroutning {controller}/{action}/{id?} för att få en intuitiv uppfattning om hur detta fungerar i praktiken. En ändring av ett värde ogiltigförklarar alla vägvärden som visas till höger. Detta återspeglar antagandet om hierarki. Om appen har ett omgivande värde för id, och åtgärden anger ett annat värde för controller:

  • id återanvänds inte eftersom {controller} är till vänster om {id?}.

Några exempel som visar den här principen:

  • Om de explicita värdena innehåller ett värde för idignoreras det omgivande värdet för id . Omgivande värden för controller och action kan användas.
  • Om de explicita värdena innehåller ett värde för actionignoreras alla omgivande värden för action . Omgivningsvärdena för controller kan användas. Om det explicita värdet för action skiljer sig från det omgivande värdet för actionid används inte värdet. Om det tydliga värdet för action är detsamma som det omgivande värdet för action, kan id värdet användas.
  • Om de explicita värdena innehåller ett värde för controllerignoreras alla omgivande värden för controller . Om det explicita värdet för controller skiljer sig från det omgivande värdet för controlleraction används inte värdena ochid. Om det explicita värdet för controller är detsamma som det omgivande värdet för controlleraction kan värdena och id användas.

Den här processen kompliceras ytterligare av förekomsten av attributvägar och dedikerade konventionella vägar. Styrenheters konventionella vägar som {controller}/{action}/{id?} specificerar en hierarki med hjälp av ruttparametrar. För dedikerade konventionella vägar och attributvägar till styrningar och Razor sidor:

  • Det finns en hierarki med vägvärden.
  • De visas inte i mallen.

I dessa fall definierar URL-generering det nödvändiga värdekonceptet . Slutpunkter som skapats av kontrollanter och Razor sidor har angivna värden som gör att vägvärdets ogiltighet kan fungera.

Ruttvärdets ogiltighetsalgoritm förklarad:

  • De nödvändiga värdenamnen kombineras med vägparametrarna och bearbetas sedan från vänster till höger.
  • För varje parameter jämförs det omgivande värdet och det explicita värdet:
    • Om det omgivande värdet och det explicita värdet är samma fortsätter processen.
    • Om det omgivande värdet finns och det explicita värdet inte är det används det omgivande värdet när URL:en genereras.
    • Om det omgivande värdet inte finns och det explicita värdet är avvisar du det omgivande värdet och alla efterföljande omgivande värden.
    • Om det omgivande värdet och det explicita värdet finns, och de två värdena är olika, avvisar du det omgivande värdet och alla efterföljande omgivande värden.

I det här läget är URL-genereringsåtgärden redo att utvärdera routningsbegränsningar. Uppsättningen godkända värden kombineras med parameterns standardvärden, vilka tillhandahålls för begränsningar. Om alla begränsningar uppfylls fortsätter operationen.

Därefter kan de godkända värdena användas för att expandera routningsmallen. Vägmallen bearbetas:

  • Från vänster till höger.
  • Varje parameter har sitt godkända värde ersatt.
  • Med följande specialfall:
    • Om de godkända värdena saknar ett värde och parametern har ett standardvärde används standardvärdet.
    • Om de godkända värdena saknar ett värde och parametern är valfri fortsätter bearbetningen.
    • Om en routningsparameter till höger om en valfri parameter som saknas har ett värde misslyckas åtgärden.
    • Sammanhängande standardvärdeparametrar och valfria parametrar komprimeras där det är möjligt.

Värden som uttryckligen anges som inte matchar ett segment av vägen läggs till i frågesträngen. Följande tabell visar resultatet när du använder routningsmallen {controller}/{action}/{id?}.

Omgivande värden Explicita värden Result
controller = "Home" action = "Om" /Home/About
controller = "Home" controller = "Beställning", action = "Om" /Order/About
controller = "Home", color = "Röd" action = "Om" /Home/About
controller = "Home" action = "Om", color = "Röd" /Home/About?color=Red

Valfri vägparameterordning

Valfria vägparametrar måste komma efter alla obligatoriska vägparametrar och literaler. I följande kod måste parametrarna id och name komma efter parametern 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 ?? ""}");
    }
}

Problem med att routningsvärdet är ogiltigt

Följande kod visar ett exempel på ett URL-genereringsschema som inte stöds av routning:

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

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

I föregående kod culture används routningsparametern för lokalisering. Önskan är att parametern culture alltid ska accepteras som ett omgivande värde. Parametern culture accepteras dock inte som ett omgivande värde på grund av hur nödvändiga värden fungerar:

  • I routningsmallen "default" är routningsparametern culture till vänster om controller, så ändringar i kommer inte att controller ogiltigförklara culture.
  • "blog" I vägmallen culture anses vägparametern vara till höger om controller, som visas i de obligatoriska värdena.

Parsa URL-sökvägar med LinkParser

Klassen LinkParser lägger till stöd för att parsa en URL-sökväg till en uppsättning vägvärden. Metoden ParsePathByEndpointName tar ett slutpunktsnamn och en URL-sökväg och returnerar en uppsättning vägvärden som extraherats från URL-sökvägen.

I följande exempelkontrollant använder åtgärden GetProduct en routningsmall med api/Products/{id} och har en Name av GetProduct:

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

I samma kontrollantklass AddRelatedProduct förväntar sig åtgärden en URL-sökväg, , pathToRelatedProductsom kan anges som en frågesträngsparameter:

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

    // ...

I föregående exempel extraherar AddRelatedProduct åtgärden id ruttvärdet från URL-sökvägen. Till exempel, med en URL-sökväg på /api/Products/1, är värdet på relatedProductId inställt till 1. Med den här metoden kan API:ets klienter använda URL-sökvägar när de refererar till resurser, utan att behöva veta hur en sådan URL är strukturerad.

Konfigurera slutpunktsmetadata

Följande länkar innehåller information om hur du konfigurerar slutpunktsmetadata:

Värdmatchning i rutter med RequireHost

RequireHost tillämpar en begränsning på den rutt som kräver den angivna värden. Parametern RequireHost eller [Värd] kan vara en:

  • Värd: www.domain.com, matchar www.domain.com med valfri port.
  • Värd med jokertecken: *.domain.com, matchar www.domain.com, subdomain.domain.com, eller www.subdomain.domain.com på valfri port.
  • Port: *:5000, matchar port 5000 med valfri värd.
  • Värd och port: www.domain.com:5000 eller *.domain.com:5000, matchar värd och port.

Flera parametrar kan anges med RequireHost eller [Host]. Villkoret matchar värdar som är giltiga för någon av parametrarna. Matchar till exempel [Host("domain.com", "*.domain.com")], domain.com, www.domain.com, och subdomain.domain.com.

Följande kod använder RequireHost för att kräva den angivna värden på rutten:

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

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

Följande kod använder [Host] attributet på kontrollanten för att kräva någon av de angivna värdarna:

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

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

[Host] När attributet tillämpas på både kontrollanten och åtgärdsmetoden:

  • Attributet för åtgärden används.
  • Kontrollantattributet ignoreras.

Warning

API som förlitar sig på värdrubriken, till exempel HttpRequest.Host och RequireHost, är föremål för potentiell förfalskning av klienter.

Använd någon av följande metoder för att förhindra förfalskning av värd- och portar:

Routningsgrupper

Tilläggsmetoden MapGroup hjälper till att organisera grupper av slutpunkter med ett gemensamt prefix. Det minskar repetitiv kod och gör det möjligt att anpassa hela grupper av slutpunkter med ett enda anrop till metoder som RequireAuthorization och WithMetadata som lägger till slutpunktsmetadata.

Följande kod skapar till exempel två liknande grupper av slutpunkter:

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

I det här scenariot kan du använda en relativ adress för Location-rubriken i 201 Created-resultatet.

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

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

Den första gruppen med slutpunkter matchar endast begäranden som är prefix med /public/todos och är tillgängliga utan autentisering. Den andra gruppen med slutpunkter matchar endast begäranden som är prefix med /private/todos och kräver autentisering.

QueryPrivateTodos slutpunktfilterfabriken är en lokal funktion som ändrar rout-hanterarens TodoDb parametrar för att tillåta åtkomst till och lagring av privata data.

Routningsgrupper stöder också kapslade grupper och komplexa prefixmönster med vägparametrar och begränsningar. I följande exempel kan routhanteraren som mappas till gruppen user fånga de {org}- och {group}-routningsparametrar som definierats i de yttre gruppens prefix.

Prefixet kan också vara tomt. Detta kan vara användbart för att lägga till slutpunktsmetadata eller filter till en grupp slutpunkter utan att ändra vägmönstret.

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

Att lägga till filter eller metadata i en grupp fungerar på samma sätt som att lägga till dem individuellt i varje slutpunkt innan du lägger till extra filter eller metadata som kan ha lagts till i en inre grupp eller en specifik slutpunkt.

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

I exemplet ovan loggar det yttre filtret den inkommande begäran före det inre filtret trots att det lades till tvåa. Eftersom filtren tillämpades på olika grupper spelar det ingen roll vilken ordning de lades till i förhållande till varandra. Orderfiltren som läggs till spelar roll om de tillämpas på samma grupp eller specifika slutpunkt.

En begäran om att /outer/inner/ loggar följande:

/outer group filter
/inner group filter
MapGet filter

Prestandavägledning för routning

När en app har prestandaproblem misstänks routning ofta vara problemet. Anledningen till att routning misstänks är att ramverk som kontrollers och Razor Pages rapporterar hur lång tid som spenderas inom ramverket i sina loggningsmeddelanden. När det finns en betydande skillnad mellan den tid som rapporteras av kontrollanter och den totala tiden för begäran:

  • Utvecklare eliminerar sin appkod som källa till problemet.
  • Det är vanligt att anta att routning är orsaken.

Routning är prestandatestad med tusentals slutpunkter. Det är osannolikt att en typisk app kommer att stöta på ett prestandaproblem bara genom att vara för stor. Den vanligaste grundorsaken till långsamma routningsprestanda är vanligtvis ett dåligt fungerande anpassad middleware.

Följande kodexempel visar en grundläggande teknik för att begränsa fördröjningskällan:

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

För att schemalägga dirigeringen:

  • Alternativt infoga mellanprogrammen med en kopia av tidsmellanprogrammet som visas i föregående kod.
  • Lägg till en unik identifierare för att korrelera tidsdata med koden.

Det här är ett grundläggande sätt att begränsa fördröjningen när den är betydande, till exempel mer än 10ms. Subtrahera Time 2 från Time 1 rapporterar tiden som spenderats i UseRouting mellanprogrammet.

Följande kod använder en mer kompakt metod för föregående tidskod:

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

Potentiellt dyra routningsfunktioner

Följande lista ger en inblick i routningsfunktioner som är relativt dyra jämfört med grundläggande routningsmallar:

  • Reguljära uttryck: Det går att skriva reguljära uttryck som är komplexa eller har lång körningstid med en liten mängd indata.
  • Komplicerade segment ({x}-{y}-{z}):
    • Är betydligt dyrare än att parsa ett vanligt URL-sökvägssegment.
    • Resulterar i att många fler delsträngar kommer att allokeras.
  • Synkron dataåtkomst: Många komplexa appar har databasåtkomst som en del av routningen. Använd utökningspunkter som MatcherPolicy och EndpointSelectorContext, som är asynkrona.

Vägledning för stora routningstabeller

Som standard använder ASP.NET Core en routningsalgoritm som byter minne mot CPU-tid. Detta har den fina effekten att vägmatchningstiden endast beror på längden på sökvägen som ska matchas och inte antalet vägar. Den här metoden kan dock vara potentiellt problematisk i vissa fall, när appen har ett stort antal vägar (i tusental) och det finns en hög mängd variabelprefix i vägarna. Om rutterna har parametrar i tidiga segment av rutten, som {parameter}/some/literal.

Det är osannolikt att en app stöter på en situation där detta är ett problem om inte:

  • Det finns ett stort antal vägar i appen med det här mönstret.
  • Det finns ett stort antal vägar i appen.

Så här avgör du om en app stöter på problemet med den stora routningstabellen

  • Det finns två symptom att leta efter:
    • Appen startar långsamt vid första användningen.
      • Observera att detta är obligatoriskt men inte tillräckligt. Det finns många andra problem som inte är routningsproblem än vad som kan orsaka långsam appstart. Kontrollera om villkoret nedan är korrekt för att fastställa att appen körs i den här situationen.
    • Appen förbrukar mycket minne under starten och en minnesdump visar ett stort antal Microsoft.AspNetCore.Routing.Matching.DfaNode instanser.

Så här åtgärdar du det här problemet

Det finns flera tekniker och optimeringar som kan tillämpas på vägar som till stor del förbättrar det här scenariot:

  • Tillämpa routningsbegränsningar på dina parametrar, till exempel {parameter:int}, {parameter:guid}, {parameter:regex(\\d+)}osv. där det är möjligt.
    • På så sätt kan routningsalgoritmen internt optimera de strukturer som används för matchning och drastiskt minska det minne som används.
    • I de allra flesta fall räcker detta för att återgå till ett acceptabelt beteende.
  • Ändra vägarna för att flytta parametrar till senare segment i mallen.
    • Detta minskar antalet möjliga "vägar" för att matcha en slutpunkt med en given väg.
  • Använd en dynamisk rutt och utför mappningen till en kontroller/sida dynamiskt.
    • Detta kan uppnås med hjälp av MapDynamicControllerRoute och MapDynamicPageRoute.

Kortslutningsmellanprogram efter routning

När routningen matchar en slutpunkt låter den vanligtvis resten av pipelinen för mellanprogram köras innan slutpunktslogik anropas. Tjänster kan minska resursanvändningen genom att filtrera bort kända begäranden tidigt i pipelinen. Använd ShortCircuit-tilläggsmetoden för att få routning att anropa slutpunktslogik omedelbart och sedan avsluta begäran. En viss väg kanske till exempel inte behöver gå igenom autentisering eller CORS-mellanprogram. Följande exempel avbryter förfrågningar som matchar den /short-circuit rutten.

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

Metoden ShortCircuit(IEndpointConventionBuilder, Nullable<Int32>) kan också ta en statuskod.

Använd metoden MapShortCircuit för att konfigurera genvägar för flera rutter samtidigt genom att skicka en parameterlista med URL-prefix till den. Webbläsare och robotar avsöker till exempel ofta servrar efter välkända sökvägar som robots.txt och favicon.ico. Om appen inte har dessa filer kan en kodrad konfigurera båda vägarna:

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

MapShortCircuit returnerar IEndpointConventionBuilder så att ytterligare vägbegränsningar som värdfiltrering kan läggas till i den.

Metoderna ShortCircuit och MapShortCircuit påverkar inte mellanprogram som placerats före UseRouting. Om du försöker använda dessa metoder med slutpunkter som också har [Authorize] eller [RequireCors] metadata kommer begäranden att misslyckas med en InvalidOperationException. Dessa metadata tillämpas av [Authorize] eller [EnableCors] attribut eller av RequireCors eller RequireAuthorization metoder.

Om du vill se effekten av kortslutningsmellanprogram anger du loggningskategorin "Microsoft" till "Information" i appsettings.Development.json:

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

Kör följande kod:

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

Följande exempel är från de konsolloggar som skapas genom att köra endpointen /. Den innehåller utdata från loggningsmellanprogrammet:

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

Följande exempel är från när man kör slutpunkten /short-circuit. Den har inget från loggningsmellanprogrammet eftersom mellanprogrammet var kortslutet:

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.

Vägledning för biblioteksförfattare

Det här avsnittet innehåller vägledning för biblioteksförfattare som bygger ovanpå routning. Den här informationen är avsedd att säkerställa att apputvecklare har en bra upplevelse med hjälp av bibliotek och ramverk som utökar routningen.

Definiera slutpunkter

Om du vill skapa ett ramverk som använder routning för URL-matchning börjar du med att definiera en användarupplevelse som bygger ovanpå UseEndpoints.

BYGG ovanpå IEndpointRouteBuilder. På så sätt kan användarna skapa ditt ramverk med andra ASP.NET Core-funktioner utan förväxling. Varje ASP.NET Core-mall innehåller routning. Anta att routning är närvarande och bekant för användare.

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

app.MapHealthChecks("/healthz");

Returnera en förseglad betongtyp från ett anrop till MapMyFramework(...) som implementerar IEndpointConventionBuilder. De flesta ramverksmetoder Map... följer det här mönstret. Gränssnittet IEndpointConventionBuilder :

  • Tillåter att metadata skapas.
  • Är mål för en mängd olika tilläggsmetoder.

Om du deklarerar din egen typ kan du lägga till dina egna ramverksspecifika funktioner i byggaren. Det är okej att omsluta ett ramverksdeklarerat byggobjekt och vidarebefordra anrop till det.

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

app.MapHealthChecks("/healthz");

ÖVERVÄG att skriva din egen EndpointDataSource. EndpointDataSource är en grundläggande konstruktion för att deklarera och uppdatera en samling slutpunkter. EndpointDataSource är ett kraftfullt API som används av kontrollanter och Razor sidor. Mer information finns i Dynamisk slutpunktsroutning.

Routningstesterna har ett grundläggande exempel på en datakälla som inte uppdateras.

ÖVERVÄG att implementera GetGroupedEndpoints. Detta ger fullständig kontroll över gruppkonventioner som körs och de slutliga metadata på de grupperade slutpunkterna. Detta gör till exempel att anpassade EndpointDataSource implementeringar kan köra slutpunktsfilter som lagts till i grupper.

Försök INTE att registrera en EndpointDataSource som standard. Kräv att användare registrerar ditt ramverk i UseEndpoints. Routningsfilosofin är att ingenting ingår som standard, och det UseEndpoints är platsen där slutpunkter ska registreras.

Skapa routningsintegrerade mellanprogram

ÖVERVÄG att definiera metadatatyper som ett gränssnitt.

Gör det möjligt att använda metadatatyper som ett attribut för klasser och metoder.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

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

Ramverk som kontrollanter och Razor sidor stöder tillämpning av metadataattribut på typer och metoder. Om du deklarerar metadatatyper:

  • Gör dem tillgängliga som attribut.
  • De flesta användare är bekanta med att tillämpa attribut.

Om du deklarerar en metadatatyp som ett gränssnitt läggs ett annat flexibelt lager till:

  • Gränssnitt är sammansättningsbara.
  • Utvecklare kan deklarera sina egna typer som kombinerar flera principer.

Gör det möjligt att åsidosätta metadata, som du ser i följande exempel:

[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() { }
}

Det bästa sättet att följa dessa riktlinjer är att undvika att definiera markörmetadata:

  • Leta inte bara efter förekomsten av en metadatatyp.
  • Definiera en egenskap för metadata och kontrollera egenskapen.

Metadatasamlingen är ordnad och möjliggör att överskrida efter prioritet. När det gäller kontrollanter är metadata för åtgärdsmetoden mest specifika.

Gör mellanprogram användbara med och utan routning:

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

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

Som ett exempel på den här riktlinjen bör du överväga UseAuthorization mellanprogrammet. Med mellanprogrammet för auktorisering kan du ange en reservpolicy. Om den anges, gäller återställningsprincipen för båda:

  • Slutpunkter utan en angiven policy.
  • Begäranden som inte överensstämmer med en slutpunkt.

Detta gör auktoriseringsmellanprogrammet användbart utanför routningskontexten. Mellanprogrammet för auktorisering kan användas för traditionell mellanprogramprogrammering.

Felsökningsdiagnostik

För detaljerade routningsdiagnostikutdata anger du Logging:LogLevel:Microsoft till Debug. I utvecklingsmiljön anger du loggnivån i appsettings.Development.json:

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

Ytterligare resurser

Routning ansvarar för att matcha inkommande HTTP-begäranden och skicka dessa begäranden till appens körbara slutpunkter. Slutpunkter är appens enheter för körbar kod för hantering av begäranden. Slutpunkter definieras i appen och konfigureras när appen startas. Slutpunktsmatchningsprocessen kan extrahera värden från begärans URL och ange dessa värden för bearbetning av begäranden. Med hjälp av slutpunktsinformation från appen kan routning också generera URL:er som mappar till slutpunkter.

Appar kan konfigurera routning med hjälp av:

  • Controllers
  • Razor sidor
  • SignalR
  • gRPC-tjänster
  • Slutpunktsaktiverat mellanprogram , till exempel hälsokontroller.
  • Delegater och lambdas som registrerats med routning.

Den här artikeln beskriver information på låg nivå om ASP.NET Core-routning. Information om hur du konfigurerar routning:

Grunderna för routning

Följande kod visar ett grundläggande exempel på routning:

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

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

app.Run();

Föregående exempel innehåller en enskild slutpunkt med hjälp av MapGet metoden:

  • När en HTTP-begäran GET skickas till rot-URL:en /:
    • Begärandedelegaten utförs.
    • Hello World! skrivs till HTTP-svaret.
  • Om begärandemetoden inte är GET eller om rot-URL:en inte är /, matchar ingen rutt och en HTTP 404 returneras.

Routning använder ett par mellanprogram, registrerade av UseRouting och UseEndpoints:

  • UseRouting lägger till routningsmatchning till pipelinen för mellanprogram. Det här mellanprogrammet tittar på den uppsättning slutpunkter som definierats i appen och väljer den bästa matchningen baserat på begäran.
  • UseEndpoints lägger till slutpunktskörning i pipelinen för mellanprogram. Den kör delegeringen som är kopplad till den valda slutpunkten.

Appar behöver vanligtvis inte anropa UseRouting eller UseEndpoints. WebApplicationBuilder konfigurerar en middleware-pipeline som omsluter den middleware som lagts till i Program.cs med UseRouting och UseEndpoints. Appar kan dock ändra i vilken UseRouting ordning och UseEndpoints körs genom att anropa dessa metoder explicit. Följande kod gör till exempel ett explicit anrop till UseRouting:

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

app.UseRouting();

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

I koden ovan:

  • Anropet till app.Use registrerar ett anpassat mellanprogram som körs i början av pipelinen.
  • Anropet till UseRouting konfigurerar mellanprogrammet för ruttmatchning att köras efter det anpassade mellanprogrammet.
  • Slutpunkten som registrerats med MapGet körs i slutet av pipeline.

Om föregående exempel inte innehöll ett anrop till UseRoutingskulle det anpassade mellanprogrammet köras efter vägen som matchar mellanprogrammet.

Endpoints

Metoden MapGet används för att definiera en slutpunkt. En slutpunkt är något som kan vara:

  • Vald genom att matcha URL:en och HTTP-metoden.
  • Utförs genom att köra delegeringen.

Ändpunkter som kan matchas och köras av appen konfigureras i UseEndpoints. Till exempel ansluter MapGet, MapPost och liknande metoder förbinder ombud för begäranden till routningssystemet. Ytterligare metoder kan användas för att ansluta ASP.NET Core Framework-funktioner till routningssystemet:

I följande exempel visas routning med en mer avancerad vägmall:

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

Strängen /hello/{name:alpha} är en vägmall. En vägmall används för att konfigurera hur slutpunkten matchas. I det här fallet matchar mallen:

  • En URL som /hello/Docs
  • Alla URL-sökvägar som börjar med /hello/ följt av en sekvens med alfabetiska tecken. :alpha tillämpar en vägbegränsning som endast matchar alfabetiska tecken. Routningsbegränsningar beskrivs senare i den här artikeln.

Det andra segmentet i URL-sökvägen: {name:alpha}

I följande exempel visas routning med hälsokontroller och auktorisering:

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

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

Föregående exempel visar hur:

  • Mellanprogrammet för auktorisering kan användas med routning.
  • Slutpunkter kan användas för att konfigurera auktoriseringsbeteende.

Anropet MapHealthChecks lägger till en slutpunkt för hälsokontroll. Länkning RequireAuthorization till det här anropet kopplar en auktoriseringsprincip till slutpunkten.

Anropar UseAuthentication och UseAuthorization lägger till mellanprogrammet för autentisering och auktorisering. Dessa mellanprogram placeras mellan UseRouting och UseEndpoints så att de kan:

  • Se vilken slutpunkt som har valts av UseRouting.
  • Tillämpa en auktoriseringsprincip innan UseEndpoints skickas till slutpunkten.

Slutpunktsmetadata

I föregående exempel finns det två slutpunkter, men endast slutpunkten för hälsokontroll har en auktoriseringsprincip kopplad. Om begäran matchar slutpunkten /healthzför hälsokontroll utförs en auktoriseringskontroll. Detta visar att slutpunkter kan ha extra data kopplade till sig. Dessa extra data kallas slutpunktsmetadata:

  • Metadata kan bearbetas av routningsmedvetna mellanprogram.
  • Metadata kan vara av valfri .NET-typ.

Routningsbegrepp

Routningssystemet bygger på pipelinen för mellanprogram genom att lägga till det kraftfulla slutpunktskonceptet . Slutpunkter representerar enheter i appens funktioner som skiljer sig från varandra när det gäller routning, auktorisering och valfritt antal ASP.NET Core-system.

ASP.NET Core-slutpunktsdefinition

En ASP.NET Core-slutpunkt är:

Följande kod visar hur du hämtar och inspekterar slutpunkten som matchar den aktuella begäran:

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

Slutpunkten, om den är markerad, kan hämtas från HttpContext. Dess egenskaper kan inspekteras. Slutpunktsobjekt är oföränderliga och kan inte ändras när de har skapats. Den vanligaste typen av slutpunkt är en RouteEndpoint. RouteEndpoint innehåller information som gör att den kan väljas av routningssystemet.

I föregående kod konfigurerar app.Use en inline-mellanprogram.

Följande kod visar att det, beroende på var app.Use som anropas i pipelinen, kanske inte finns någon slutpunkt:

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

Föregående exempel lägger till Console.WriteLine instruktioner som visar om en slutpunkt har valts eller inte. För tydlighetens skull tilldelar exemplet ett visningsnamn till den angivna / slutpunkten.

Föregående exempel innehåller även anrop till UseRouting och UseEndpoints för att styra exakt när dessa mellanprogram körs i pipelinen.

Kör den här koden med en URL på / visar:

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

Om du kör den här koden med andra URL:ar visas:

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

Dessa utdata visar att:

  • Slutpunkten är alltid null innan UseRouting anropas.
  • Om en matchning hittas är slutpunkten inte null mellan UseRouting och UseEndpoints.
  • Mellanprogrammet UseEndpoints är terminal när en matchning hittas. Terminalmellanprogram definieras senare i den här artikeln.
  • Mellanprogrammet efter UseEndpoints körs endast när ingen matchning hittas.

Mellanprogrammet UseRouting använder SetEndpoint metoden för att koppla slutpunkten till den aktuella kontexten. Det går att ersätta UseRouting mellanprogrammet med anpassad logik och ändå få fördelarna med att använda slutpunkter. Ändpunkter är ett grundläggande element på låg nivå som mellanvaror och är inte kopplade till routningens implementering. De flesta appar behöver inte ersättas UseRouting med anpassad logik.

Mellanprogrammet UseEndpoints är utformat för att användas tillsammans med UseRouting mellanprogrammet. Kärnlogiken för att utföra en slutpunkt är inte komplicerad. Använd GetEndpoint för att hämta slutpunkten och anropa sedan dess RequestDelegate egenskap.

Följande kod visar hur mellanprogram kan påverka eller reagera på routning:

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 { }

Föregående exempel visar två viktiga begrepp:

  • Mellanprogram kan köras tidigare UseRouting för att ändra de data som routningen körs på.
  • Mellanprogram kan köras mellan UseRouting och UseEndpoints för att bearbeta resultatet av routingen innan slutpunkten körs.
    • Mellanprogram som körs mellan UseRouting och UseEndpoints:
      • Inspekterar vanligtvis metadata för att förstå slutpunkterna.
      • Fattar ofta säkerhetsbeslut, som görs av UseAuthorization och UseCors.
    • Kombinationen av mellanprogram och metadata gör det möjligt att konfigurera principer per slutpunkt.

Föregående kod visar ett exempel på ett anpassat mellanprogram som stöder principer per slutpunkt. Mellanprogrammet skriver en revisionslogg av åtkomst av känsliga data till konsolen. Mellanprogrammet kan konfigureras för att granska en slutpunkt med RequiresAuditAttribute metadata. Det här exemplet visar ett opt-in-mönster där endast slutpunkter som är markerade som känsliga granskas. Det är möjligt att definiera den här logiken omvänt och granska allt som inte är markerat som säkert, till exempel. Slutpunktsmetadatasystemet är flexibelt. Den här logiken kan utformas på vilket sätt som helst som passar användningsfallet.

Föregående exempelkod är avsedd att demonstrera de grundläggande begreppen för slutpunkter. Exemplet är inte avsett för produktionsanvändning. En mer fullständig version av ett mellanprogram för granskningsloggar skulle:

  • Logga in på en fil eller databas.
  • Inkludera information som användaren, IP-adressen, namnet på den känsliga slutpunkten med mera.

Metadata för revisionspolicy RequiresAuditAttribute definieras som en Attribute för att enklare kunna använda klassbaserade ramverk som kontroller och SignalR. När du använder route to code:

  • Metadata bifogas med ett builder-API.
  • Klassbaserade ramverk innehåller alla attribut på motsvarande metod och klass när du skapar slutpunkter.

Metodtipsen för metadatatyper är att definiera dem antingen som gränssnitt eller attribut. Gränssnitt och attribut tillåter återanvändning av kod. Metadatasystemet är flexibelt och medför inga begränsningar.

Jämför terminalmellanprogram med routning

I följande exempel visas både terminalmellanprogram och routning:

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

Formatet för mellanprogram som visas med Approach 1: är terminalmellanprogram. Det kallas terminalmellanprogram eftersom det utför en matchande åtgärd:

  • Matchningsåtgärden i föregående exempel är Path == "/" för mellanprogrammet och Path == "/Routing" för routning.
  • När en matchning lyckas, utför den vissa funktioner och returnerar istället för att anropa next-mellanprogrammet.

Det kallas terminalmellanprogram eftersom det avslutar sökningen, kör vissa funktioner och sedan returnerar.

I följande lista jämförs terminalmellanprogram med routning:

  • Båda metoderna gör det möjligt att avsluta bearbetningspipelinen:
    • Mellanprogram avslutar pipelinen genom att returnera i stället för att nextanropa .
    • Slutpunkter är alltid terminaler.
  • Med terminalmellanprogram kan du placera mellanprogrammet på en godtycklig plats i pipelinen:
  • Med terminalmellanprogram kan godtycklig kod avgöra när mellanprogrammet matchar:
    • Anpassad vägmatchningskod kan vara utförlig och svår att skriva korrekt.
    • Routning ger enkla lösningar för typiska appar. De flesta appar kräver inte anpassad vägmatchningskod.
  • Slutpunktsgränssnitt med mellanprogram som UseAuthorization och UseCors.
    • Användning av ett terminalmellanprogram med UseAuthorization eller UseCors kräver manuell koppling med auktoriseringssystemet.

En slutpunkt definierar båda:

  • Ett ombud för att bearbeta begäranden.
  • En samling godtyckliga metadata. Metadata används för att implementera övergripande problem baserat på principer och konfiguration som är kopplade till varje slutpunkt.

Terminalmellanprogram kan vara ett effektivt verktyg, men kan kräva:

  • En betydande mängd kodning och testning.
  • Manuell integrering med andra system för att uppnå önskad flexibilitetsnivå.

Överväg att integrera med routning innan du skriver ett terminalmellanprogram.

Befintliga terminalmellanprogram som integreras med Map eller MapWhen kan vanligtvis omvandlas till en routingmedveten slutpunkt. MapHealthChecks visar mönstret för router-ware:

  • Skriv en tilläggsmetod på IEndpointRouteBuilder.
  • Skapa en pipeline för kapslade mellanprogram med CreateApplicationBuilder.
  • Koppla mellanprogrammet till den nya pipelinen. I det här fallet UseHealthChecks.
  • Build mellanprogramspipelinen till en RequestDelegate.
  • Anropa Map och ange den nya pipelinen för mellanprogram.
  • Returnera builder-objektet som tillhandahålls av Map från tilläggsmetoden.

Följande kod visar användning av MapHealthChecks:

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

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

Föregående exempel visar varför det är viktigt att returnera builder-objektet. När byggobjektet returneras kan apputvecklaren konfigurera principer som auktorisering för slutpunkten. I det här exemplet har mellanprogrammet för hälsokontroller ingen direkt integrering med auktoriseringssystemet.

Metadatasystemet skapades som svar på de problem som uppstått av utökningsförfattare med hjälp av terminalmellanprogram. Det är problematiskt för varje mellanprogram att implementera sin egen integrering med auktoriseringssystemet.

URL-matchning

  • Är den process genom vilken routningen matchar en inkommande begäran till en slutpunkt.
  • Baseras på data i URL-sökvägen och rubrikerna.
  • Kan utökas för att överväga alla data i begäran.

När ett routningsmellanprogram körs anger det en Endpoint och dirigerar värden till en begäransfunktionHttpContext från den aktuella begäran:

  • Genom att anropa HttpContext.GetEndpoint hämtas slutpunkten.
  • HttpRequest.RouteValues hämtar samlingen av routvärden.

Mellanprogram körs efter att routningsmellanprogram har möjlighet att inspektera slutpunkten och vidta åtgärder. Till exempel kan ett mellanprogramvara för auktorisering granska och utvärdera slutpunktens metadatasamling för en auktoriseringspolicy. När alla mellanprogram i pipelinen för bearbetning av begäranden har körts anropas den valda slutpunktens ombud.

Routningssystemet i slutpunktsroutningen ansvarar för alla leveransbeslut. Eftersom mellanprogrammet tillämpar principer baserat på den valda slutpunkten är det viktigt att:

  • Alla beslut som kan påverka sändningen eller tillämpningen av säkerhetsprinciper fattas i routningssystemet.

Warning

För bakåtkompatibilitet, när en Controller eller Razor Pages-slutpunktsdelegat körs, anges egenskaperna till lämpliga värden baserat på den bearbetning som hittills utförts av RouteContext.RouteData-begäran.

Typen RouteContext kommer att markeras som föråldrad i en framtida version:

  • Migrera RouteData.Values till HttpRequest.RouteValues.
  • Migrera RouteData.DataTokens för att hämta IDataTokensMetadata från slutpunktsmetadata.

URL-matchning fungerar i en konfigurerbar uppsättning faser. I varje fas är utdata en uppsättning matchningar. Mängden matcher kan begränsas ytterligare under nästa fas. Routningsimplementeringen garanterar inte någon bearbetningsordning för matchande slutpunkter. Alla möjliga matchningar bearbetas samtidigt. Url-matchningsfaserna sker i följande ordning. ASP.NET Core:

  1. Bearbetar URL-sökvägen mot uppsättningen slutpunkter och deras vägmallar och samlar in alla matchningar.
  2. Tar den föregående listan och tar bort matchningar som inte uppfyller ruttbegränsningar som tillämpas.
  3. Tar föregående lista och tar bort matchningar som inte uppfyller kraven för MatcherPolicy instanser.
  4. Använder EndpointSelector för att fatta ett slutgiltigt beslut från föregående lista.

Listan över slutpunkter prioriteras enligt:

Alla matchande ändpunkter bearbetas i varje fas tills den EndpointSelector har nåtts. EndpointSelector är den sista fasen. Den väljer den högsta prioritetsslutpunkten från matchningarna som den bästa matchningen. Om det finns andra matchningar med samma prioritet som den bästa matchningen, kommer ett tvetydigt matchningsundantag att utlösas.

Routningsprioriteten beräknas baserat på att en mer specifik vägmall får högre prioritet. Tänk till exempel på mallarna /hello och /{message}:

  • Båda matchar URL-sökvägen /hello.
  • /hello är mer specifik och därför högre prioritet.

I allmänhet gör routningsprioriteten ett bra jobb med att välja den bästa matchningen för de typer av URL-scheman som används i praktiken. Använd Order endast när det behövs för att undvika tvetydigheter.

På grund av de typer av utökningsbarhet som tillhandahålls av routning är det inte möjligt för routningssystemet att beräkna de tvetydiga vägarna i förväg. Överväg ett exempel som routningsmallarna /{message:alpha} och /{message:int}:

  • Villkoret alpha matchar endast alfabetiska tecken.
  • Villkoret int matchar endast tal.
  • Dessa mallar har samma vägprioritet, men det finns ingen enskild URL som båda matchar.
  • Om routningssystemet rapporterade ett tvetydighetsfel vid starten skulle det blockera det här giltiga användningsfallet.

Warning

Ordningen på åtgärderna i UseEndpoints påverkar inte beteendet för routning, med ett undantag. MapControllerRoute och MapAreaRoute tilldela automatiskt ett ordervärde till sina slutpunkter baserat på den ordning som de anropas. Detta simulerar långvarigt beteende för kontrollanter utan att routningssystemet ger samma garantier som äldre routningsimplementeringar.

Slutpunktsroutning i ASP.NET Core:

  • Har inte begreppet rutter.
  • Ger inte ordergarantier. Alla slutpunkter bearbetas samtidigt.

Routningsmallens prioritet och slutpunktsmarkeringsordning

Prioritet för routningsmallar är ett system som tilldelar varje vägmall ett värde baserat på hur specifik den är. Prioritet för routningsmall:

  • Undviker behovet av att justera ordningen på slutpunkter i vanliga fall.
  • Försöker matcha allmänna förväntningar på routingbeteende.

Du kan till exempel överväga mallar /Products/List och /Products/{id}. Det är rimligt att anta att det /Products/List är en bättre matchning än /Products/{id} för URL-sökvägen /Products/List. Detta fungerar eftersom literalsegmentet /List anses ha bättre prioritet än parametersegmentet /{id}.

Information om hur prioritet fungerar är kopplat till hur routningsmallar definieras:

  • Mallar med fler segment anses vara mer specifika.
  • Ett segment med literaltext anses vara mer specifikt än ett parametersegment.
  • Ett parametersegment med en begränsning anses vara mer specifikt än ett utan.
  • Ett komplext segment anses vara lika specifikt som ett parametersegment med en begränsning.
  • Catch-all-parametrar är de minst specifika. Se catch-all i avsnittet Routningsmallar för viktig information om catch-all-vägar.

Begrepp för URL-generering

URL-generering:

  • Är den process genom vilken routning kan skapa en URL-sökväg baserat på en uppsättning vägvärden.
  • Tillåter en logisk separation mellan slutpunkter och URL:er som har åtkomst till dem.

Slutpunktsroutning innehåller API:et LinkGenerator . LinkGenerator är en singleton-tjänst som är tillgänglig från DI. API LinkGenerator kan användas utanför kontexten för en exekverande begäran. Mvc.IUrlHelper och scenarier som förlitar sig på IUrlHelper, till exempel Tag Helpers, HTML Helpers och Action Results, använder API:et LinkGenerator internt för att tillhandahålla funktioner för länkgenerering.

Länkgeneratorn backas upp av begreppet adress - och adressscheman. Ett adressschema är ett sätt att fastställa de slutpunkter som ska beaktas för länkgenerering. Till exempel implementeras scenarier med routningnamn och routvärden som många användare är bekanta med från kontroller och Razor sidor såsom ett adressschema.

Länkgeneratorn kan länka till styrenheter och Razor sidor via följande tilläggsmetoder:

Överbelastningar av dessa metoder tar emot argument som inkluderar HttpContext. Dessa metoder är funktionellt likvärdiga med Url.Action och Url.Page, men ger ytterligare flexibilitet och alternativ.

Metoderna GetPath* liknar Url.Action mest och Url.Page, eftersom de genererar en URI som innehåller en absolut sökväg. Metoderna GetUri* genererar alltid en absolut URI som innehåller ett schema och en värd. De metoder som accepterar en HttpContext genererar en URI i kontexten för den körande begäran. Värden ambienta för väg, URL-bassökväg, protokoll och värd från den körande begäran används om de inte åsidosätts.

LinkGenerator anropas med en adress. Generera en URI sker i två steg:

  1. En adress är bunden till en lista över slutpunkter som matchar adressen.
  2. Varje slutpunkts RoutePattern utvärderas tills ett vägmönster som matchar de angivna värdena hittas. Resultatet kombineras med de andra URI-delarna som levereras till länkgeneratorn och returneras.

Metoderna som tillhandahålls av LinkGenerator stöder standardfunktioner för länkgenerering för alla typer av adresser. Det enklaste sättet att använda länkgeneratorn är genom tilläggsmetoder som utför åtgärder för en viss adresstyp:

Tilläggsmetod Description
GetPathByAddress Genererar en URI med en absolut sökväg baserat på de angivna värdena.
GetUriByAddress Genererar en absolut URI baserat på de angivna värdena.

Warning

Var uppmärksam på följande konsekvenser av att anropa LinkGenerator metoder:

  • Använd GetUri* tilläggsmetoder med försiktighet i en appkonfiguration som inte validerar headern för inkommande begäranden. Host Om rubriken för inkommande begäranden inte verifieras kan ej betrodda begärandeindata skickas tillbaka till klienten i URI:er i en vy eller sida. Vi rekommenderar att alla produktionsappar konfigurerar sin server för att kontrollera Host headern mot kända giltiga värden.

  • Använd LinkGenerator med försiktighet i mellanprogram i kombination med Map eller MapWhen. Map* ändrar basvägen för den körande begäran, vilket påverkar utdata från länkgenereringen. LinkGenerator Alla API:er tillåter att du anger en bassökväg. Ange en tom bassökväg för att ta bort Map* effekten på länkgenereringen.

Exempel på mellanprogram

I följande exempel använder ett mellanprogram API:et LinkGenerator för att skapa en länk till en åtgärdsmetod som visar lagerprodukter. Använda länkgeneratorn genom att mata in den i en klass och anrop GenerateLink är tillgängligt för alla klasser i en app:

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

Routningsmallar

Token inom {} definierar ruttparametrar som är bundna om rutten matchas. Mer än en vägparameter kan definieras i ett vägsegment, men vägparametrarna måste avgränsas med ett literalvärde. Till exempel:

{controller=Home}{action=Index}

är inte en giltig väg eftersom det inte finns något literalvärde mellan {controller} och {action}. Routningsparametrarna måste ha ett namn och kan ha ytterligare attribut angivna.

Annan literaltext än vägparametrar (till exempel {id}) och sökvägsavgränsaren / måste matcha texten i URL:en. Textmatchning är skiftlägesokänslig och baseras på den avkodade representationen av URL:ens sökväg. Om du vill matcha en literalvägsparameter avgränsare { eller }kan du undvika avgränsare genom att upprepa tecknet. Till exempel {{ eller }}.

Asterisk * eller dubbel asterisk **:

  • Kan användas som ett prefix till en vägparameter för att binda till resten av URI:n.
  • Kallas för catch-all-parametrar. Till exempel blog/{**slug}:
    • Matchar alla URI:er som börjar med blog/ och har ett värde som följer den.
    • Värdet som följer efter blog/ tilldelas som värde för slug-routen.

Warning

En parameter kan matcha vägar felaktigt på grund av en bugg i routning. Appar som påverkas av den här buggen har följande egenskaper:

  • En allomfattande rutt, till exempel {**slug}"
  • Catch-all-vägen matchar inte begäranden som den ska matcha.
  • Om du tar bort andra rutter börjar den allomfattande rutten att fungera.

Se GitHub-buggar 18677 och 16579 till exempel fall som drabbat den här buggen.

En anmälningskorrigering för den här buggen finns i .NET Core 3.1.301 eller senare SDK. Följande kod anger en intern växel som åtgärdar felet:

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

Catch-all-parametrar kan även matcha den tomma strängen.

Parametern catch-all undflyr lämpliga tecken när vägen används för att generera en URL, inklusive sökvägsavgränsningstecken / . Till exempel genererar rutten foo/{*path} med ruttvärden { path = "my/path" }foo/my%2Fpath. Observera det undantagna snedstrecket. Använd routningsparameterprefixet ** för att avgränsa sökvägens avgränsningstecken. Vägen foo/{**path} med { path = "my/path" } genererar foo/my/path.

URL-mönster som försöker avbilda ett filnamn med ett valfritt filnamnstillägg har ytterligare överväganden. Överväg till exempel mallen files/{filename}.{ext?}. När värden för båda filename och ext finns fylls båda värdena i. Om det bara finns ett värde för filename i URL:en matchar vägen eftersom avslutandet . är valfritt. Följande URL:er matchar den här vägen:

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

Routningsparametrar kan ha standardvärden som anges genom att ange standardvärdet efter parameternamnet avgränsat med ett likhetstecken (=). Definierar {controller=Home}Home till exempel som standardvärde för controller. Standardvärdet används om inget värde finns i URL:en för parametern. Routningsparametrar görs valfria genom att ett frågetecken (?) läggs till i slutet av parameternamnet. Till exempel id?. Skillnaden mellan valfria värden och standardvägsparametrar är:

  • En routningsparameter med ett standardvärde genererar alltid ett värde.
  • En valfri parameter har endast ett värde när ett värde tillhandahålls av begärande-URL:en.

Ruttparametrarna kan ha begränsningar som måste matcha ruttvärdet som är bundet från URL:en. Att lägga till : och villkorsnamn efter namnet på routningsparametern anger en infogad begränsning för en routningsparameter. Om villkoret kräver argument omges de av parenteser (...) efter villkorsnamnet. Du kan ange flera inline-begränsningar genom att lägga till en annan : och ett annat begränsningsnamn.

Villkorsnamnet och argumenten IInlineConstraintResolver skickas till tjänsten för att skapa en instans av IRouteConstraint som ska användas i URL-bearbetning. Routningsmallen blog/{article:minlength(10)} anger till exempel en minlength begränsning med argumentet 10. Mer information om routningsbegränsningar och en lista över de begränsningar som tillhandahålls av ramverket finns i avsnittet Routningsbegränsningar .

Routningsparametrar kan också ha parametertransformatorer. Parametertransformatorer transformerar en parameters värde vid generering av länkar och matchande åtgärder och sidor till URL:er. Precis som begränsningar kan parametertransformatorer läggas till direkt i en routparameter genom att lägga till ett : och ett transformeringsnamn efter routparameternamnet. Routningsmallen blog/{article:slugify} anger till exempel en slugify transformerare. Mer information om parametertransformatorer finns i avsnittet Parametertransformatorer .

I följande tabell visas exempel på routningsmallar och deras beteende:

Routningsmall Exempel på matchande URI Förfrågan-URI:n...
hello /hello Matchar endast den ensamma sökvägen /hello.
{Page=Home} / Matchar och anger Page till Home.
{Page=Home} /Contact Matchar och anger Page till Contact.
{controller}/{action}/{id?} /Products/List Mappar till kontroller Products och åtgärd List.
{controller}/{action}/{id?} /Products/Details/123 Mappar till kontrollanten Products och Details åtgärden medid värdet 123.
{controller=Home}/{action=Index}/{id?} / Mappar till Home-kontrollern och Index-metoden. id ignoreras.
{controller=Home}/{action=Index}/{id?} /Products Mappar till Products-kontrollern och Index-metoden. id ignoreras.

Att använda en mall är vanligtvis den enklaste metoden för routning. Begränsningar och standardvärden kan också anges utanför vägmallen.

Komplexa segment

Komplexa segment bearbetas genom matchning av literalavgränsare från höger till vänster på ett icke-girigt sätt. Till exempel [Route("/a{b}c{d}")] är ett komplext segment. Komplexa segment fungerar på ett visst sätt som måste tolkas för att kunna använda dem. Exemplet i det här avsnittet visar varför komplexa segment bara fungerar bra när avgränsartexten inte visas i parametervärdena. Att använda en regex och sedan extrahera värdena manuellt behövs för mer komplexa fall.

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Det här är en sammanfattning av de steg som routningen utför med mallen /a{b}c{d} och URL-sökvägen /abcd. | Används för att visualisera hur algoritmen fungerar:

  • Den första literalen, från höger till vänster, är c. Så /abcd söks från höger och hittar /ab|c|d.
  • Allt till höger (d) matchas nu med routningsparametern {d}.
  • Nästa literal, från höger till vänster, är a. Så /ab|c|d genomsöks från där vi slutade, sedan hittas a/|a|b|c|d.
  • Värdet till höger (b) matchas nu med routningsparametern {b}.
  • Det finns ingen kvarvarande text och ingen kvarvarande mall för rutt, så det här är en matchning.

Här är ett exempel på ett negativt ärende med samma mall /a{b}c{d} och URL-sökvägen /aabcd. | Används för att visualisera hur algoritmen fungerar. Det här fallet är inte en matchning, vilket förklaras av samma algoritm:

  • Den första literalen, från höger till vänster, är c. Så /aabcd söks från höger och hittar /aab|c|d.
  • Allt till höger (d) matchas nu med routningsparametern {d}.
  • Nästa literal, från höger till vänster, är a. Så /aab|c|d genomsöks från där vi slutade, sedan hittas a/a|a|b|c|d.
  • Värdet till höger (b) matchas nu med routningsparametern {b}.
  • I det här läget finns det fortfarande text a, men algoritmen har slut på vägmall för analys, så detta stämmer inte.

Eftersom matchningsalgoritmen inte är girig:

  • Den matchar den minsta möjliga mängden text i varje steg.
  • Alla fall där avgränsarvärdet visas i parametervärdena resulterar i att det inte matchar.

Reguljära uttryck ger mycket mer kontroll över deras matchande beteende.

Girig matchning, också kallad försiktig matchning, matchar den största möjliga strängen. Icke-giriga matchar den minsta möjliga strängen.

Routning med specialtecken

Routning med specialtecken kan leda till oväntade resultat. Tänk dig till exempel en kontrollant med följande åtgärdsmetod:

[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;
}

När string id innehåller följande kodade värden kan oväntade resultat inträffa:

ASCII Encoded
/ %2F
+

Routningsparametrar är inte alltid URL-avkodade. Det här problemet kan åtgärdas i framtiden. Mer information finns i det här GitHub-problemet;

Vägbegränsningar

Routningsbegränsningar körs när en matchning har hittats för den inkommande URL:en och URL-sökvägen tokeniseras till ruttvärden. Routningsbegränsningar inspekterar vanligtvis det vägvärde som är associerat via routningsmallen och fattar ett sant eller falskt beslut om huruvida värdet är acceptabelt. Vissa routningsbegränsningar använder data utanför vägvärdet för att överväga om begäran kan dirigeras. Till exempel HttpMethodRouteConstraint kan acceptera eller avvisa en begäran baserat på dess HTTP-verb. Begränsningar används i routningsbegäranden och länkgenerering.

Warning

Använd inte begränsningar för indataverifiering. Om begränsningar används för indataverifiering resulterar ogiltiga indata i ett 404 svar som inte hittades. Ogiltiga indata bör generera en 400 felaktig begäran med ett lämpligt felmeddelande. Routningsbegränsningar används för att särskilja liknande rutter, inte för att verifiera inmatningar för en viss rutt.

I följande tabell visas exempel på vägbegränsningar och deras förväntade beteende:

constraint Example Exempelmatchningar Notes
int {id:int} 123456789, -123456789 Matchar alla heltal
bool {active:bool} true, FALSE Matchar true eller false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Matchar ett giltigt DateTime värde i den invarianta kulturen. Se föregående varning.
decimal {price:decimal} 49.99, -1,000.01 Matchar ett giltigt decimal värde i den invarianta kulturen. Se föregående varning.
double {weight:double} 1.234, -1,001.01e8 Matchar ett giltigt double värde i den invarianta kulturen. Se föregående varning.
float {weight:float} 1.234, -1,001.01e8 Matchar ett giltigt float värde i den invarianta kulturen. Se föregående varning.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Matchar ett giltigt Guid värde
long {ticks:long} 123456789, -123456789 Matchar ett giltigt long värde
minlength(value) {username:minlength(4)} Rick Strängen måste innehålla minst 4 tecken
maxlength(value) {filename:maxlength(8)} MyFile Strängen får inte innehålla fler än 8 tecken
length(length) {filename:length(12)} somefile.txt Strängen måste vara exakt 12 tecken lång
length(min,max) {filename:length(8,16)} somefile.txt Strängen får vara minst 8 och högst 16 tecken lång
min(value) {age:min(18)} 19 Heltalsvärdet måste vara minst 18
max(value) {age:max(120)} 91 Heltalsvärdet får inte vara mer än 120
range(min,max) {age:range(18,120)} 91 Heltalsvärdet måste vara minst 18 men högst 120
alpha {name:alpha} Rick Strängen måste bestå av ett eller flera alfabetiska tecken, och vara skiftlägesokänslig, a-z.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 Strängen måste matcha det reguljära uttrycket. Se tips om hur du definierar ett reguljärt uttryck.
required {name:required} Rick Används för att framtvinga att ett icke-parametervärde finns under URL-generering

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Flera, kolonavgränsade begränsningar kan tillämpas på en enskild parameter. Följande villkor begränsar till exempel en parameter till ett heltalsvärde på 1 eller högre:

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

Warning

Routningsbegränsningar som verifierar URL:en och konverteras till en CLR-typ använder alltid den invarianta kulturen. Till exempel konvertering till CLR-typen int eller DateTime. Dessa begränsningar förutsätter att URL:en inte kan lokaliseras. De ramverksbaserade vägbegränsningarna ändrar inte de värden som lagras i vägvärden. Alla vägvärden som parsas från URL:en lagras som strängar. Villkoret float försöker till exempel konvertera vägvärdet till en flyttal, men det konverterade värdet används bara för att verifiera att det kan konverteras till en flyttal.

Reguljära uttryck i begränsningar

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Reguljära uttryck kan anges som infogade begränsningar med hjälp av routningsbegränsningen regex(...) . Metoder i MapControllerRoute familjen accepterar också en objektliteral av begränsningar. Om formuläret används tolkas strängvärden som reguljära uttryck.

Följande kod använder en infogad regex-begränsning:

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

Följande kod använder en objektliteral för att ange en regex-begränsning:

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

Ramverket ASP.NET Core lägger RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant till konstruktorn för reguljära uttryck. Se RegexOptions en beskrivning av dessa medlemmar.

Reguljära uttryck använder avgränsare och token som liknar dem som används vid routning och C#-språket. Token för reguljära uttryck måste vara undantagna. Om du vill använda reguljära uttryck ^\d{3}-\d{2}-\d{4}$ i en infogad begränsning använder du något av följande:

  • Ersätt \ tecken som anges i strängen som \\ tecken i C#-källfilen för att undvika strängens \ escape-tecken.
  • Verbatim strängliteraler.

Om du vill undvika dirigeringsparametertecken {, }, [, ], dubblar du tecknen i uttrycket, {{till exempel , }}, [[, . ]] I följande tabell visas ett reguljärt uttryck och dess undantagna version:

Reguljärt uttryck Undantagna reguljära uttryck
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Reguljära uttryck som används i routning börjar ofta med ^ tecknet och matchar strängens startposition. Uttrycken slutar ofta med $ tecknet och matchar slutet av strängen. Tecknen ^ och $ ser till att det reguljära uttrycket matchar värdet för hela routningsparametern. Utan ^- och $-tecknen matchar det reguljära uttrycket vilken delsträng som helst i strängen, vilket ofta är oönskat. Följande tabell innehåller exempel och förklarar varför de matchar eller inte matchar:

Expression String Match Comment
[a-z]{2} hello Yes Matchningar för delsträngar
[a-z]{2} 123abc456 Yes Matchningar för delsträngar
[a-z]{2} mz Yes Matchar uttryck
[a-z]{2} MZ Yes Inte skiftlägeskänslig
^[a-z]{2}$ hello No Se ^ och $ ovan
^[a-z]{2}$ 123abc456 No Se ^ och $ ovan

Mer information om syntax för reguljära uttryck finns i Reguljära .NET Framework-uttryck.

Om du vill begränsa en parameter till en känd uppsättning möjliga värden använder du ett reguljärt uttryck. Till exempel {action:regex(^(list|get|create)$)} matchar endast routningsvärdet action till list, geteller create. Om den skickas in i begränsningsordboken, så är strängen ^(list|get|create)$ likvärdig. Begränsningar som skickas i villkorsordlistan som inte matchar någon av de kända begränsningarna behandlas också som reguljära uttryck. Begränsningar som skickas i en mall som inte matchar någon av de kända begränsningarna behandlas inte som reguljära uttryck.

Begränsningar för anpassad rutt

Anpassade routningsbegränsningar kan skapas genom att implementera IRouteConstraint gränssnittet. Gränssnittet IRouteConstraint innehåller Match, som returnerar true om villkoret är uppfyllt och false på annat sätt.

Anpassade routningsbegränsningar behövs sällan. Innan du implementerar en anpassad vägbegränsning bör du överväga alternativ, till exempel modellbindning.

Mappen ASP.NET Core Constraints innehåller bra exempel på hur du skapar begränsningar. Till exempel GuidRouteConstraint.

Om du vill använda en anpassad IRouteConstraintmåste routningsbegränsningstypen registreras med appens ConstraintMap i tjänstcontainern. A ConstraintMap är en ordlista som mappar routningsbegränsningsnycklar till IRouteConstraint implementeringar som validerar dessa begränsningar. En app"s ConstraintMap kan uppdateras i Program.cs antingen som en del av ett AddRouting-anrop eller genom att konfigurera RouteOptions direkt med builder.Services.Configure<RouteOptions>. Till exempel:

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

Föregående villkor tillämpas i följande kod:

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

Implementeringen av NoZeroesRouteConstraint förhindrar 0 att den används i en vägparameter:

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

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Föregående kod:

  • Förhindrar 0 i {id} segmentet av rutten.
  • Visas som ett grundläggande exempel på hur du implementerar en anpassad begränsning. Den bör inte användas i en produktionsapp.

Följande kod är en bättre metod för att förhindra att en id innehållande en 0 bearbetas:

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

    return Content(id);
}

Föregående kod har följande fördelar jämfört med NoZeroesRouteConstraint metoden:

  • Det kräver ingen anpassad begränsning.
  • Det returnerar ett mer beskrivande fel när vägparametern innehåller 0.

Parametertransformatorer

Parametertransformatorer:

Till exempel, en anpassad slugify-parametertransformator i vägmönster blog\{article:slugify} med Url.Action(new { article = "MyTestArticle" }) genererar blog\my-test-article.

Överväg följande IOutboundParameterTransformer implementering:

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

Om du vill använda en parametertransformator i ett vägmönster konfigurerar du den med hjälp av ConstraintMap i Program.cs:

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

ASP.NET Core-ramverket använder parametertransformatorer för att transformera den URI där en slutpunkt löser problemet. Till exempel transformerar parametertransformatorer de vägvärden som används för att matcha en area, controller, actionoch page:

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

Med föregående vägmall matchas åtgärden SubscriptionManagementController.GetAll med URI /subscription-management/get-all:n . En parametertransformator ändrar inte de vägvärden som används för att generera en länk. Till exempel Url.Action("GetAll", "SubscriptionManagement") ger /subscription-management/get-all.

ASP.NET Core tillhandahåller API-konventioner för användning av parametertransformatorer med genererade vägar:

Referens för URL-generering

Det här avsnittet innehåller en referens för algoritmen som implementeras av URL-generering. I praktiken använder de flesta komplexa exempel på URL-generering styrenheter eller Razor sidor. Mer information finns i routning i kontrollanter .

URL-genereringsprocessen börjar med ett anrop till LinkGenerator.GetPathByAddress eller en liknande metod. Metoden tillhandahålls med en adress, en uppsättning vägvärden och eventuellt information om den aktuella begäran från HttpContext.

Det första steget är att använda adressen för att lösa en uppsättning kandidatslutpunkter med hjälp av en IEndpointAddressScheme<TAddress> som matchar adressens typ.

När uppsättningen kandidater hittas av adressschemat sorteras slutpunkterna och bearbetas iterativt tills en URL-genereringsåtgärd lyckas. URL-genereringen söker inte efter tvetydigheter. Det första resultatet som returneras är slutresultatet.

Felsöka URL-generering med loggning

Det första steget i felsökning av URL-generering för Microsoft.AspNetCore.Routing är att ställa in loggningsnivån till TRACE. LinkGenerator loggar många detaljer om bearbetningen som kan vara användbar för att felsöka problem.

Mer information om URL-generering finns i REFERENS för URL-generering .

Addresses

Adresser är det begrepp i URL-generering som används för att binda ett anrop till länkgeneratorn till en uppsättning kandidatslutpunkter.

Adresser är ett utökningsbart begrepp som levereras med två implementeringar som standard:

  • Använda slutpunktsnamnet (string) som adress:
    • Tillhandahåller liknande funktioner som MVC:s routningsnamn.
    • IEndpointNameMetadata Använder metadatatypen.
    • Löser den angivna strängen mot metadata för alla registrerade slutpunkter.
    • Utlöser ett undantag vid start om flera slutpunkter använder samma namn.
    • Rekommenderas för generell användning utanför kontrollanter och Razor sidor.
  • Använda vägvärden (RouteValuesAddress) som adress:
    • Ger liknande funktioner som kontroller och äldre URL-generering för sidor.
    • Mycket komplext att utöka och felsöka.
    • Tillhandahåller implementeringen som används av IUrlHelper, Tag Helpers, HTML-hjälpare, åtgärdsresultat osv.

Adressschemats roll är att göra associationen mellan adressen och matchande slutpunkter enligt godtyckliga kriterier:

  • Slutpunktsnamnschemat utför en grundläggande ordlistesökning.
  • Routningsvärdena har en komplex bästa delmängd av set-algoritmen.

Omgivande värden och explicita värden

Från den aktuella begäran får routning åtkomst till routningsvärdena för den aktuella begäran HttpContext.Request.RouteValues. De värden som är associerade med den aktuella begäran kallas för omgivande värden. För tydlighetens skull refererar dokumentationen till de vägvärden som skickas in till metoder som explicita värden.

I följande exempel visas omgivande värden och explicita värden. Den innehåller omgivande värden från den aktuella begäran och explicita värden:

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

    // ...

Föregående kod:

Följande kod innehåller endast explicita värden och inga omgivande värden:

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

Föregående metod returnerar /Home/Subscribe/17

Följande kod i WidgetController returnerar /Widget/Subscribe/17:

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

Följande kod tillhandahåller kontrollern från omgivande och explicita värden i den aktuella begäran.

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

I koden ovan:

  • /Gadget/Edit/17 returneras.
  • Url hämtar IUrlHelper.
  • Action genererar en URL med en absolut sökväg för en åtgärdsmetod. URL:en innehåller det angivna action namnet och route värdena.

Följande kod innehåller omgivande värden från den aktuella begäran och explicita värden:

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

        // ...
    }
}

Den föregående koden sätter url till /Edit/17 när Redigera Razor Sida innehåller följande siddirektiv:

@page "{id:int}"

Om sidan Redigera inte innehåller "{id:int}"-routemallen, är url/Edit?id=17.

Beteendet för MVC lägger IUrlHelper till ett lager av komplexitet utöver de regler som beskrivs här:

  • IUrlHelper tillhandahåller alltid vägvärdena från den aktuella begäran som omgivande värden.
  • IUrlHelper.Action kopierar alltid aktuella action värden och controller vägvärden som explicita värden om de inte åsidosätts av utvecklaren.
  • IUrlHelper.Page kopierar alltid det aktuella page vägvärdet som ett explicit värde om det inte åsidosätts.
  • IUrlHelper.Page åsidosätter alltid det aktuella handler ruttvärdet med null som ett explicit värde såvida det inte åsidosätts.

Användarna blir ofta förvånade över beteendeinformationen för omgivande värden, eftersom MVC inte verkar följa sina egna regler. Av historiska och kompatibilitetsskäl har vissa routningsvärden som action, controller, pageoch handler ett eget specialfallsbeteende.

Motsvarande funktionalitet som tillhandahålls av LinkGenerator.GetPathByAction och LinkGenerator.GetPathByPage duplicerar dessa avvikelser i IUrlHelper för kompatibilitet.

Process för URL-generering

När uppsättningen kandidatslutpunkter hittas, algoritmen för URL-generering:

  • Bearbetar slutpunkterna iterativt.
  • Returnerar det första lyckade resultatet.

Det första steget i den här processen kallas ogiltigförklarande av routningsvärden. Ogiltigförklaring av routningsvärden är den process genom vilken routning avgör vilka routningsvärden från bakgrundsvärden som ska användas och vilka som ska ignoreras. Varje omgivande värde beaktas och kombineras antingen med explicita värden eller ignoreras.

Det bästa sättet att förstå rollen av omgivande värden är att de försöker spara skrivande för programutvecklare i vissa vanliga situationer. Traditionellt är scenarier där omgivande värden är användbara relaterade till MVC:

  • När du länkar till en annan åtgärd i samma kontrollant behöver inte kontrollantnamnet anges.
  • När du länkar till en annan kontrollant i samma område behöver du inte ange områdesnamnet.
  • När du länkar till samma åtgärdsmetod behöver du inte ange vägvärden.
  • När du länkar till en annan del av appen vill du inte överföra routningsvärden som inte har någon betydelse i den delen av appen.

Anrop till LinkGenerator eller IUrlHelper som returnerar null orsakas vanligtvis av bristande förståelse av ogiltigförklaring av routningsvärden. Felsöka routningsvärdets ogiltighet genom att uttryckligen ange fler av vägvärdena för att se om det löser problemet.

Ogiltigförklaring av routvärden bygger på antagandet att appens URL-schema är hierarkiskt, med en hierarki som bildas från vänster till höger. Överväg den grundläggande mallen för styrenhetsroutning {controller}/{action}/{id?} för att få en intuitiv uppfattning om hur detta fungerar i praktiken. En ändring av ett värde ogiltigförklarar alla vägvärden som visas till höger. Detta återspeglar antagandet om hierarki. Om appen har ett omgivande värde för id, och åtgärden anger ett annat värde för controller:

  • id återanvänds inte eftersom {controller} är till vänster om {id?}.

Några exempel som visar den här principen:

  • Om de explicita värdena innehåller ett värde för idignoreras det omgivande värdet för id . Omgivande värden för controller och action kan användas.
  • Om de explicita värdena innehåller ett värde för actionignoreras alla omgivande värden för action . Omgivningsvärdena för controller kan användas. Om det explicita värdet för action skiljer sig från det omgivande värdet för actionid används inte värdet. Om det tydliga värdet för action är detsamma som det omgivande värdet för action, kan id värdet användas.
  • Om de explicita värdena innehåller ett värde för controllerignoreras alla omgivande värden för controller . Om det explicita värdet för controller skiljer sig från det omgivande värdet för controlleraction används inte värdena ochid. Om det explicita värdet för controller är detsamma som det omgivande värdet för controlleraction kan värdena och id användas.

Den här processen kompliceras ytterligare av förekomsten av attributvägar och dedikerade konventionella vägar. Styrenheters konventionella vägar som {controller}/{action}/{id?} specificerar en hierarki med hjälp av ruttparametrar. För dedikerade konventionella vägar och attributvägar till styrningar och Razor sidor:

  • Det finns en hierarki med vägvärden.
  • De visas inte i mallen.

I dessa fall definierar URL-generering det nödvändiga värdekonceptet . Slutpunkter som skapats av kontrollanter och Razor sidor har angivna värden som gör att vägvärdets ogiltighet kan fungera.

Ruttvärdets ogiltighetsalgoritm förklarad:

  • De nödvändiga värdenamnen kombineras med vägparametrarna och bearbetas sedan från vänster till höger.
  • För varje parameter jämförs det omgivande värdet och det explicita värdet:
    • Om det omgivande värdet och det explicita värdet är samma fortsätter processen.
    • Om det omgivande värdet finns och det explicita värdet inte är det används det omgivande värdet när URL:en genereras.
    • Om det omgivande värdet inte finns och det explicita värdet är avvisar du det omgivande värdet och alla efterföljande omgivande värden.
    • Om det omgivande värdet och det explicita värdet finns, och de två värdena är olika, avvisar du det omgivande värdet och alla efterföljande omgivande värden.

I det här läget är URL-genereringsåtgärden redo att utvärdera routningsbegränsningar. Uppsättningen godkända värden kombineras med parameterns standardvärden, vilka tillhandahålls för begränsningar. Om alla begränsningar uppfylls fortsätter operationen.

Därefter kan de godkända värdena användas för att expandera routningsmallen. Vägmallen bearbetas:

  • Från vänster till höger.
  • Varje parameter har sitt godkända värde ersatt.
  • Med följande specialfall:
    • Om de godkända värdena saknar ett värde och parametern har ett standardvärde används standardvärdet.
    • Om de godkända värdena saknar ett värde och parametern är valfri fortsätter bearbetningen.
    • Om en routningsparameter till höger om en valfri parameter som saknas har ett värde misslyckas åtgärden.
    • Sammanhängande standardvärdeparametrar och valfria parametrar komprimeras där det är möjligt.

Värden som uttryckligen anges som inte matchar ett segment av vägen läggs till i frågesträngen. Följande tabell visar resultatet när du använder routningsmallen {controller}/{action}/{id?}.

Omgivande värden Explicita värden Result
controller = "Home" action = "Om" /Home/About
controller = "Home" controller = "Beställning", action = "Om" /Order/About
controller = "Home", color = "Röd" action = "Om" /Home/About
controller = "Home" action = "Om", color = "Röd" /Home/About?color=Red

Valfri vägparameterordning

Valfria vägparametrar måste komma efter alla obligatoriska vägparametrar. I följande kod måste parametrarna id och name komma efter parametern 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 ?? ""}");
    }
}

Problem med att routningsvärdet är ogiltigt

Följande kod visar ett exempel på ett URL-genereringsschema som inte stöds av routning:

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

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

I föregående kod culture används routningsparametern för lokalisering. Önskan är att parametern culture alltid ska accepteras som ett omgivande värde. Parametern culture accepteras dock inte som ett omgivande värde på grund av hur nödvändiga värden fungerar:

  • I routningsmallen "default" är routningsparametern culture till vänster om controller, så ändringar i kommer inte att controller ogiltigförklara culture.
  • "blog" I vägmallen culture anses vägparametern vara till höger om controller, som visas i de obligatoriska värdena.

Parsa URL-sökvägar med LinkParser

Klassen LinkParser lägger till stöd för att parsa en URL-sökväg till en uppsättning vägvärden. Metoden ParsePathByEndpointName tar ett slutpunktsnamn och en URL-sökväg och returnerar en uppsättning vägvärden som extraherats från URL-sökvägen.

I följande exempelkontrollant använder åtgärden GetProduct en routningsmall med api/Products/{id} och har en Name av GetProduct:

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

I samma kontrollantklass AddRelatedProduct förväntar sig åtgärden en URL-sökväg, , pathToRelatedProductsom kan anges som en frågesträngsparameter:

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

    // ...

I föregående exempel extraherar AddRelatedProduct åtgärden id ruttvärdet från URL-sökvägen. Till exempel, med en URL-sökväg på /api/Products/1, är värdet på relatedProductId inställt till 1. Med den här metoden kan API:ets klienter använda URL-sökvägar när de refererar till resurser, utan att behöva veta hur en sådan URL är strukturerad.

Konfigurera slutpunktsmetadata

Följande länkar innehåller information om hur du konfigurerar slutpunktsmetadata:

Värdmatchning i rutter med RequireHost

RequireHost tillämpar en begränsning på den rutt som kräver den angivna värden. Parametern RequireHost eller [Värd] kan vara en:

  • Värd: www.domain.com, matchar www.domain.com med valfri port.
  • Värd med jokertecken: *.domain.com, matchar www.domain.com, subdomain.domain.com, eller www.subdomain.domain.com på valfri port.
  • Port: *:5000, matchar port 5000 med valfri värd.
  • Värd och port: www.domain.com:5000 eller *.domain.com:5000, matchar värd och port.

Flera parametrar kan anges med RequireHost eller [Host]. Villkoret matchar värdar som är giltiga för någon av parametrarna. Matchar till exempel [Host("domain.com", "*.domain.com")], domain.com, www.domain.com, och subdomain.domain.com.

Följande kod använder RequireHost för att kräva den angivna värden på rutten:

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

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

Följande kod använder [Host] attributet på kontrollanten för att kräva någon av de angivna värdarna:

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

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

[Host] När attributet tillämpas på både kontrollanten och åtgärdsmetoden:

  • Attributet för åtgärden används.
  • Kontrollantattributet ignoreras.

Routningsgrupper

Tilläggsmetoden MapGroup hjälper till att organisera grupper av slutpunkter med ett gemensamt prefix. Det minskar repetitiv kod och gör det möjligt att anpassa hela grupper av slutpunkter med ett enda anrop till metoder som RequireAuthorization och WithMetadata som lägger till slutpunktsmetadata.

Följande kod skapar till exempel två liknande grupper av slutpunkter:

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

I det här scenariot kan du använda en relativ adress för Location-rubriken i 201 Created-resultatet.

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

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

Den första gruppen med slutpunkter matchar endast begäranden som är prefix med /public/todos och är tillgängliga utan autentisering. Den andra gruppen med slutpunkter matchar endast begäranden som är prefix med /private/todos och kräver autentisering.

QueryPrivateTodos slutpunktfilterfabriken är en lokal funktion som ändrar rout-hanterarens TodoDb parametrar för att tillåta åtkomst till och lagring av privata data.

Routningsgrupper stöder också kapslade grupper och komplexa prefixmönster med vägparametrar och begränsningar. I följande exempel kan routhanteraren som mappas till gruppen user fånga de {org}- och {group}-routningsparametrar som definierats i de yttre gruppens prefix.

Prefixet kan också vara tomt. Detta kan vara användbart för att lägga till slutpunktsmetadata eller filter till en grupp slutpunkter utan att ändra vägmönstret.

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

Att lägga till filter eller metadata i en grupp fungerar på samma sätt som att lägga till dem individuellt i varje slutpunkt innan du lägger till extra filter eller metadata som kan ha lagts till i en inre grupp eller en specifik slutpunkt.

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

I exemplet ovan loggar det yttre filtret den inkommande begäran före det inre filtret trots att det lades till tvåa. Eftersom filtren tillämpades på olika grupper spelar det ingen roll vilken ordning de lades till i förhållande till varandra. Orderfiltren som läggs till spelar roll om de tillämpas på samma grupp eller specifika slutpunkt.

En begäran om att /outer/inner/ loggar följande:

/outer group filter
/inner group filter
MapGet filter

Prestandavägledning för routning

När en app har prestandaproblem misstänks routning ofta vara problemet. Anledningen till att routning misstänks är att ramverk som kontrollers och Razor Pages rapporterar hur lång tid som spenderas inom ramverket i sina loggningsmeddelanden. När det finns en betydande skillnad mellan den tid som rapporteras av kontrollanter och den totala tiden för begäran:

  • Utvecklare eliminerar sin appkod som källa till problemet.
  • Det är vanligt att anta att routning är orsaken.

Routning är prestandatestad med tusentals slutpunkter. Det är osannolikt att en typisk app kommer att stöta på ett prestandaproblem bara genom att vara för stor. Den vanligaste grundorsaken till långsamma routningsprestanda är vanligtvis ett dåligt fungerande anpassad middleware.

Följande kodexempel visar en grundläggande teknik för att begränsa fördröjningskällan:

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

För att schemalägga dirigeringen:

  • Alternativt infoga mellanprogrammen med en kopia av tidsmellanprogrammet som visas i föregående kod.
  • Lägg till en unik identifierare för att korrelera tidsdata med koden.

Det här är ett grundläggande sätt att begränsa fördröjningen när den är betydande, till exempel mer än 10ms. Subtrahera Time 2 från Time 1 rapporterar tiden som spenderats i UseRouting mellanprogrammet.

Följande kod använder en mer kompakt metod för föregående tidskod:

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

Potentiellt dyra routningsfunktioner

Följande lista ger en inblick i routningsfunktioner som är relativt dyra jämfört med grundläggande routningsmallar:

  • Reguljära uttryck: Det går att skriva reguljära uttryck som är komplexa eller har lång körningstid med en liten mängd indata.
  • Komplicerade segment ({x}-{y}-{z}):
    • Är betydligt dyrare än att parsa ett vanligt URL-sökvägssegment.
    • Resulterar i att många fler delsträngar kommer att allokeras.
  • Synkron dataåtkomst: Många komplexa appar har databasåtkomst som en del av routningen. Använd utökningspunkter som MatcherPolicy och EndpointSelectorContext, som är asynkrona.

Vägledning för stora routningstabeller

Som standard använder ASP.NET Core en routningsalgoritm som byter minne mot CPU-tid. Detta har den fina effekten att vägmatchningstiden endast beror på längden på sökvägen som ska matchas och inte antalet vägar. Den här metoden kan dock vara potentiellt problematisk i vissa fall, när appen har ett stort antal vägar (i tusental) och det finns en hög mängd variabelprefix i vägarna. Om rutterna har parametrar i tidiga segment av rutten, som {parameter}/some/literal.

Det är osannolikt att en app stöter på en situation där detta är ett problem om inte:

  • Det finns ett stort antal vägar i appen med det här mönstret.
  • Det finns ett stort antal vägar i appen.

Så här avgör du om en app stöter på problemet med den stora routningstabellen

  • Det finns två symptom att leta efter:
    • Appen startar långsamt vid första användningen.
      • Observera att detta är obligatoriskt men inte tillräckligt. Det finns många andra problem som inte är routningsproblem än vad som kan orsaka långsam appstart. Kontrollera om villkoret nedan är korrekt för att fastställa att appen körs i den här situationen.
    • Appen förbrukar mycket minne under starten och en minnesdump visar ett stort antal Microsoft.AspNetCore.Routing.Matching.DfaNode instanser.

Så här åtgärdar du det här problemet

Det finns flera tekniker och optimeringar som kan tillämpas på vägar som till stor del förbättrar det här scenariot:

  • Tillämpa routningsbegränsningar på dina parametrar, till exempel {parameter:int}, {parameter:guid}, {parameter:regex(\\d+)}osv. där det är möjligt.
    • På så sätt kan routningsalgoritmen internt optimera de strukturer som används för matchning och drastiskt minska det minne som används.
    • I de allra flesta fall räcker detta för att återgå till ett acceptabelt beteende.
  • Ändra vägarna för att flytta parametrar till senare segment i mallen.
    • Detta minskar antalet möjliga "vägar" för att matcha en slutpunkt med en given väg.
  • Använd en dynamisk rutt och utför mappningen till en kontroller/sida dynamiskt.
    • Detta kan uppnås med hjälp av MapDynamicControllerRoute och MapDynamicPageRoute.

Vägledning för biblioteksförfattare

Det här avsnittet innehåller vägledning för biblioteksförfattare som bygger ovanpå routning. Den här informationen är avsedd att säkerställa att apputvecklare har en bra upplevelse med hjälp av bibliotek och ramverk som utökar routningen.

Definiera slutpunkter

Om du vill skapa ett ramverk som använder routning för URL-matchning börjar du med att definiera en användarupplevelse som bygger ovanpå UseEndpoints.

BYGG ovanpå IEndpointRouteBuilder. På så sätt kan användarna skapa ditt ramverk med andra ASP.NET Core-funktioner utan förväxling. Varje ASP.NET Core-mall innehåller routning. Anta att routning är närvarande och bekant för användare.

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

app.MapHealthChecks("/healthz");

Returnera en förseglad betongtyp från ett anrop till MapMyFramework(...) som implementerar IEndpointConventionBuilder. De flesta ramverksmetoder Map... följer det här mönstret. Gränssnittet IEndpointConventionBuilder :

  • Tillåter att metadata skapas.
  • Är mål för en mängd olika tilläggsmetoder.

Om du deklarerar din egen typ kan du lägga till dina egna ramverksspecifika funktioner i byggaren. Det är okej att omsluta ett ramverksdeklarerat byggobjekt och vidarebefordra anrop till det.

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

app.MapHealthChecks("/healthz");

ÖVERVÄG att skriva din egen EndpointDataSource. EndpointDataSource är en grundläggande konstruktion för att deklarera och uppdatera en samling slutpunkter. EndpointDataSource är ett kraftfullt API som används av kontrollanter och Razor sidor.

Routningstesterna har ett grundläggande exempel på en datakälla som inte uppdateras.

ÖVERVÄG att implementera GetGroupedEndpoints. Detta ger fullständig kontroll över gruppkonventioner som körs och de slutliga metadata på de grupperade slutpunkterna. Detta gör till exempel att anpassade EndpointDataSource implementeringar kan köra slutpunktsfilter som lagts till i grupper.

Försök INTE att registrera en EndpointDataSource som standard. Kräv att användare registrerar ditt ramverk i UseEndpoints. Routningsfilosofin är att ingenting ingår som standard, och det UseEndpoints är platsen där slutpunkter ska registreras.

Skapa routningsintegrerade mellanprogram

ÖVERVÄG att definiera metadatatyper som ett gränssnitt.

Gör det möjligt att använda metadatatyper som ett attribut för klasser och metoder.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

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

Ramverk som kontrollanter och Razor sidor stöder tillämpning av metadataattribut på typer och metoder. Om du deklarerar metadatatyper:

  • Gör dem tillgängliga som attribut.
  • De flesta användare är bekanta med att tillämpa attribut.

Om du deklarerar en metadatatyp som ett gränssnitt läggs ett annat flexibelt lager till:

  • Gränssnitt är sammansättningsbara.
  • Utvecklare kan deklarera sina egna typer som kombinerar flera principer.

Gör det möjligt att åsidosätta metadata, som du ser i följande exempel:

[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() { }
}

Det bästa sättet att följa dessa riktlinjer är att undvika att definiera markörmetadata:

  • Leta inte bara efter förekomsten av en metadatatyp.
  • Definiera en egenskap för metadata och kontrollera egenskapen.

Metadatasamlingen är ordnad och möjliggör att överskrida efter prioritet. När det gäller kontrollanter är metadata för åtgärdsmetoden mest specifika.

Gör mellanprogram användbara med och utan routning:

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

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

Som ett exempel på den här riktlinjen bör du överväga UseAuthorization mellanprogrammet. Med mellanprogrammet för auktorisering kan du ange en reservpolicy. Om den anges, gäller återställningsprincipen för båda:

  • Slutpunkter utan en angiven policy.
  • Begäranden som inte överensstämmer med en slutpunkt.

Detta gör auktoriseringsmellanprogrammet användbart utanför routningskontexten. Mellanprogrammet för auktorisering kan användas för traditionell mellanprogramprogrammering.

Felsökningsdiagnostik

För detaljerade routningsdiagnostikutdata anger du Logging:LogLevel:Microsoft till Debug. I utvecklingsmiljön anger du loggnivån i appsettings.Development.json:

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

Ytterligare resurser

Routning ansvarar för att matcha inkommande HTTP-begäranden och skicka dessa begäranden till appens körbara slutpunkter. Slutpunkter är appens enheter för körbar kod för hantering av begäranden. Slutpunkter definieras i appen och konfigureras när appen startas. Slutpunktsmatchningsprocessen kan extrahera värden från begärans URL och ange dessa värden för bearbetning av begäranden. Med hjälp av slutpunktsinformation från appen kan routning också generera URL:er som mappar till slutpunkter.

Appar kan konfigurera routning med hjälp av:

  • Controllers
  • Razor sidor
  • SignalR
  • gRPC-tjänster
  • Slutpunktsaktiverat mellanprogram , till exempel hälsokontroller.
  • Delegater och lambdas som registrerats med routning.

Den här artikeln beskriver information på låg nivå om ASP.NET Core-routning. Information om hur du konfigurerar routning:

Grunderna för routning

Följande kod visar ett grundläggande exempel på routning:

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

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

app.Run();

Föregående exempel innehåller en enskild slutpunkt med hjälp av MapGet metoden:

  • När en HTTP-begäran GET skickas till rot-URL:en /:
    • Begärandedelegaten utförs.
    • Hello World! skrivs till HTTP-svaret.
  • Om begärandemetoden inte är GET eller om rot-URL:en inte är /, matchar ingen rutt och en HTTP 404 returneras.

Routning använder ett par mellanprogram, registrerade av UseRouting och UseEndpoints:

  • UseRouting lägger till routningsmatchning till pipelinen för mellanprogram. Det här mellanprogrammet tittar på den uppsättning slutpunkter som definierats i appen och väljer den bästa matchningen baserat på begäran.
  • UseEndpoints lägger till slutpunktskörning i pipelinen för mellanprogram. Den kör delegeringen som är kopplad till den valda slutpunkten.

Appar behöver vanligtvis inte anropa UseRouting eller UseEndpoints. WebApplicationBuilder konfigurerar en middleware-pipeline som omsluter den middleware som lagts till i Program.cs med UseRouting och UseEndpoints. Appar kan dock ändra i vilken UseRouting ordning och UseEndpoints körs genom att anropa dessa metoder explicit. Följande kod gör till exempel ett explicit anrop till UseRouting:

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

app.UseRouting();

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

I koden ovan:

  • Anropet till app.Use registrerar ett anpassat mellanprogram som körs i början av pipelinen.
  • Anropet till UseRouting konfigurerar mellanprogrammet för ruttmatchning att köras efter det anpassade mellanprogrammet.
  • Slutpunkten som registrerats med MapGet körs i slutet av pipeline.

Om föregående exempel inte innehöll ett anrop till UseRoutingskulle det anpassade mellanprogrammet köras efter vägen som matchar mellanprogrammet.

Endpoints

Metoden MapGet används för att definiera en slutpunkt. En slutpunkt är något som kan vara:

  • Vald genom att matcha URL:en och HTTP-metoden.
  • Utförs genom att köra delegeringen.

Ändpunkter som kan matchas och köras av appen konfigureras i UseEndpoints. Till exempel ansluter MapGet, MapPost och liknande metoder förbinder ombud för begäranden till routningssystemet. Ytterligare metoder kan användas för att ansluta ASP.NET Core Framework-funktioner till routningssystemet:

I följande exempel visas routning med en mer avancerad vägmall:

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

Strängen /hello/{name:alpha} är en vägmall. En vägmall används för att konfigurera hur slutpunkten matchas. I det här fallet matchar mallen:

  • En URL som /hello/Docs
  • Alla URL-sökvägar som börjar med /hello/ följt av en sekvens med alfabetiska tecken. :alpha tillämpar en vägbegränsning som endast matchar alfabetiska tecken. Routningsbegränsningar beskrivs senare i den här artikeln.

Det andra segmentet i URL-sökvägen: {name:alpha}

I följande exempel visas routning med hälsokontroller och auktorisering:

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

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

Föregående exempel visar hur:

  • Mellanprogrammet för auktorisering kan användas med routning.
  • Slutpunkter kan användas för att konfigurera auktoriseringsbeteende.

Anropet MapHealthChecks lägger till en slutpunkt för hälsokontroll. Länkning RequireAuthorization till det här anropet kopplar en auktoriseringsprincip till slutpunkten.

Anropar UseAuthentication och UseAuthorization lägger till mellanprogrammet för autentisering och auktorisering. Dessa mellanprogram placeras mellan UseRouting och UseEndpoints så att de kan:

  • Se vilken slutpunkt som har valts av UseRouting.
  • Tillämpa en auktoriseringsprincip innan UseEndpoints skickas till slutpunkten.

Slutpunktsmetadata

I föregående exempel finns det två slutpunkter, men endast slutpunkten för hälsokontroll har en auktoriseringsprincip kopplad. Om begäran matchar slutpunkten /healthzför hälsokontroll utförs en auktoriseringskontroll. Detta visar att slutpunkter kan ha extra data kopplade till sig. Dessa extra data kallas slutpunktsmetadata:

  • Metadata kan bearbetas av routningsmedvetna mellanprogram.
  • Metadata kan vara av valfri .NET-typ.

Routningsbegrepp

Routningssystemet bygger på pipelinen för mellanprogram genom att lägga till det kraftfulla slutpunktskonceptet . Slutpunkter representerar enheter i appens funktioner som skiljer sig från varandra när det gäller routning, auktorisering och valfritt antal ASP.NET Core-system.

ASP.NET Core-slutpunktsdefinition

En ASP.NET Core-slutpunkt är:

Följande kod visar hur du hämtar och inspekterar slutpunkten som matchar den aktuella begäran:

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

Slutpunkten, om den är markerad, kan hämtas från HttpContext. Dess egenskaper kan inspekteras. Slutpunktsobjekt är oföränderliga och kan inte ändras när de har skapats. Den vanligaste typen av slutpunkt är en RouteEndpoint. RouteEndpoint innehåller information som gör att den kan väljas av routningssystemet.

I föregående kod konfigurerar app.Use en inline-mellanprogram.

Följande kod visar att det, beroende på var app.Use som anropas i pipelinen, kanske inte finns någon slutpunkt:

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

Föregående exempel lägger till Console.WriteLine instruktioner som visar om en slutpunkt har valts eller inte. För tydlighetens skull tilldelar exemplet ett visningsnamn till den angivna / slutpunkten.

Föregående exempel innehåller även anrop till UseRouting och UseEndpoints för att styra exakt när dessa mellanprogram körs i pipelinen.

Kör den här koden med en URL på / visar:

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

Om du kör den här koden med andra URL:ar visas:

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

Dessa utdata visar att:

  • Slutpunkten är alltid null innan UseRouting anropas.
  • Om en matchning hittas är slutpunkten inte null mellan UseRouting och UseEndpoints.
  • Mellanprogrammet UseEndpoints är terminal när en matchning hittas. Terminalmellanprogram definieras senare i den här artikeln.
  • Mellanprogrammet efter UseEndpoints körs endast när ingen matchning hittas.

Mellanprogrammet UseRouting använder SetEndpoint metoden för att koppla slutpunkten till den aktuella kontexten. Det går att ersätta UseRouting mellanprogrammet med anpassad logik och ändå få fördelarna med att använda slutpunkter. Ändpunkter är ett grundläggande element på låg nivå som mellanvaror och är inte kopplade till routningens implementering. De flesta appar behöver inte ersättas UseRouting med anpassad logik.

Mellanprogrammet UseEndpoints är utformat för att användas tillsammans med UseRouting mellanprogrammet. Kärnlogiken för att utföra en slutpunkt är inte komplicerad. Använd GetEndpoint för att hämta slutpunkten och anropa sedan dess RequestDelegate egenskap.

Följande kod visar hur mellanprogram kan påverka eller reagera på routning:

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 { }

Föregående exempel visar två viktiga begrepp:

  • Mellanprogram kan köras tidigare UseRouting för att ändra de data som routningen körs på.
  • Mellanprogram kan köras mellan UseRouting och UseEndpoints för att bearbeta resultatet av routingen innan slutpunkten körs.
    • Mellanprogram som körs mellan UseRouting och UseEndpoints:
      • Inspekterar vanligtvis metadata för att förstå slutpunkterna.
      • Fattar ofta säkerhetsbeslut, som görs av UseAuthorization och UseCors.
    • Kombinationen av mellanprogram och metadata gör det möjligt att konfigurera principer per slutpunkt.

Föregående kod visar ett exempel på ett anpassat mellanprogram som stöder principer per slutpunkt. Mellanprogrammet skriver en revisionslogg av åtkomst av känsliga data till konsolen. Mellanprogrammet kan konfigureras för att granska en slutpunkt med RequiresAuditAttribute metadata. Det här exemplet visar ett opt-in-mönster där endast slutpunkter som är markerade som känsliga granskas. Det är möjligt att definiera den här logiken omvänt och granska allt som inte är markerat som säkert, till exempel. Slutpunktsmetadatasystemet är flexibelt. Den här logiken kan utformas på vilket sätt som helst som passar användningsfallet.

Föregående exempelkod är avsedd att demonstrera de grundläggande begreppen för slutpunkter. Exemplet är inte avsett för produktionsanvändning. En mer fullständig version av ett mellanprogram för granskningsloggar skulle:

  • Logga in på en fil eller databas.
  • Inkludera information som användaren, IP-adressen, namnet på den känsliga slutpunkten med mera.

Metadata för revisionspolicy RequiresAuditAttribute definieras som en Attribute för att enklare kunna använda klassbaserade ramverk som kontroller och SignalR. När du använder route to code:

  • Metadata bifogas med ett builder-API.
  • Klassbaserade ramverk innehåller alla attribut på motsvarande metod och klass när du skapar slutpunkter.

Metodtipsen för metadatatyper är att definiera dem antingen som gränssnitt eller attribut. Gränssnitt och attribut tillåter återanvändning av kod. Metadatasystemet är flexibelt och medför inga begränsningar.

Jämför terminalmellanprogram med routning

I följande exempel visas både terminalmellanprogram och routning:

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

Formatet för mellanprogram som visas med Approach 1: är terminalmellanprogram. Det kallas terminalmellanprogram eftersom det utför en matchande åtgärd:

  • Matchningsåtgärden i föregående exempel är Path == "/" för mellanprogrammet och Path == "/Routing" för routning.
  • När en matchning lyckas, utför den vissa funktioner och returnerar istället för att anropa next-mellanprogrammet.

Det kallas terminalmellanprogram eftersom det avslutar sökningen, kör vissa funktioner och sedan returnerar.

I följande lista jämförs terminalmellanprogram med routning:

  • Båda metoderna gör det möjligt att avsluta bearbetningspipelinen:
    • Mellanprogram avslutar pipelinen genom att returnera i stället för att nextanropa .
    • Slutpunkter är alltid terminaler.
  • Med terminalmellanprogram kan du placera mellanprogrammet på en godtycklig plats i pipelinen:
  • Med terminalmellanprogram kan godtycklig kod avgöra när mellanprogrammet matchar:
    • Anpassad vägmatchningskod kan vara utförlig och svår att skriva korrekt.
    • Routning ger enkla lösningar för typiska appar. De flesta appar kräver inte anpassad vägmatchningskod.
  • Slutpunktsgränssnitt med mellanprogram som UseAuthorization och UseCors.
    • Användning av ett terminalmellanprogram med UseAuthorization eller UseCors kräver manuell koppling med auktoriseringssystemet.

En slutpunkt definierar båda:

  • Ett ombud för att bearbeta begäranden.
  • En samling godtyckliga metadata. Metadata används för att implementera övergripande problem baserat på principer och konfiguration som är kopplade till varje slutpunkt.

Terminalmellanprogram kan vara ett effektivt verktyg, men kan kräva:

  • En betydande mängd kodning och testning.
  • Manuell integrering med andra system för att uppnå önskad flexibilitetsnivå.

Överväg att integrera med routning innan du skriver ett terminalmellanprogram.

Befintliga terminalmellanprogram som integreras med Map eller MapWhen kan vanligtvis omvandlas till en routingmedveten slutpunkt. MapHealthChecks visar mönstret för router-ware:

  • Skriv en tilläggsmetod på IEndpointRouteBuilder.
  • Skapa en pipeline för kapslade mellanprogram med CreateApplicationBuilder.
  • Koppla mellanprogrammet till den nya pipelinen. I det här fallet UseHealthChecks.
  • Build mellanprogramspipelinen till en RequestDelegate.
  • Anropa Map och ange den nya pipelinen för mellanprogram.
  • Returnera builder-objektet som tillhandahålls av Map från tilläggsmetoden.

Följande kod visar användning av MapHealthChecks:

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

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

Föregående exempel visar varför det är viktigt att returnera builder-objektet. När byggobjektet returneras kan apputvecklaren konfigurera principer som auktorisering för slutpunkten. I det här exemplet har mellanprogrammet för hälsokontroller ingen direkt integrering med auktoriseringssystemet.

Metadatasystemet skapades som svar på de problem som uppstått av utökningsförfattare med hjälp av terminalmellanprogram. Det är problematiskt för varje mellanprogram att implementera sin egen integrering med auktoriseringssystemet.

URL-matchning

  • Är den process genom vilken routningen matchar en inkommande begäran till en slutpunkt.
  • Baseras på data i URL-sökvägen och rubrikerna.
  • Kan utökas för att överväga alla data i begäran.

När ett routningsmellanprogram körs anger det en Endpoint och dirigerar värden till en begäransfunktionHttpContext från den aktuella begäran:

  • Genom att anropa HttpContext.GetEndpoint hämtas slutpunkten.
  • HttpRequest.RouteValues hämtar samlingen av routvärden.

Mellanprogram körs efter att routningsmellanprogram har möjlighet att inspektera slutpunkten och vidta åtgärder. Till exempel kan ett mellanprogramvara för auktorisering granska och utvärdera slutpunktens metadatasamling för en auktoriseringspolicy. När alla mellanprogram i pipelinen för bearbetning av begäranden har körts anropas den valda slutpunktens ombud.

Routningssystemet i slutpunktsroutningen ansvarar för alla leveransbeslut. Eftersom mellanprogrammet tillämpar principer baserat på den valda slutpunkten är det viktigt att:

  • Alla beslut som kan påverka sändningen eller tillämpningen av säkerhetsprinciper fattas i routningssystemet.

Warning

För bakåtkompatibilitet, när en Controller eller Razor Pages-slutpunktsdelegat körs, anges egenskaperna till lämpliga värden baserat på den bearbetning som hittills utförts av RouteContext.RouteData-begäran.

Typen RouteContext kommer att markeras som föråldrad i en framtida version:

  • Migrera RouteData.Values till HttpRequest.RouteValues.
  • Migrera RouteData.DataTokens för att hämta IDataTokensMetadata från slutpunktsmetadata.

URL-matchning fungerar i en konfigurerbar uppsättning faser. I varje fas är utdata en uppsättning matchningar. Mängden matcher kan begränsas ytterligare under nästa fas. Routningsimplementeringen garanterar inte någon bearbetningsordning för matchande slutpunkter. Alla möjliga matchningar bearbetas samtidigt. Url-matchningsfaserna sker i följande ordning. ASP.NET Core:

  1. Bearbetar URL-sökvägen mot uppsättningen slutpunkter och deras vägmallar och samlar in alla matchningar.
  2. Tar den föregående listan och tar bort matchningar som inte uppfyller ruttbegränsningar som tillämpas.
  3. Tar föregående lista och tar bort matchningar som inte uppfyller kraven för MatcherPolicy instanser.
  4. Använder EndpointSelector för att fatta ett slutgiltigt beslut från föregående lista.

Listan över slutpunkter prioriteras enligt:

Alla matchande ändpunkter bearbetas i varje fas tills den EndpointSelector har nåtts. EndpointSelector är den sista fasen. Den väljer den högsta prioritetsslutpunkten från matchningarna som den bästa matchningen. Om det finns andra matchningar med samma prioritet som den bästa matchningen, kommer ett tvetydigt matchningsundantag att utlösas.

Routningsprioriteten beräknas baserat på att en mer specifik vägmall får högre prioritet. Tänk till exempel på mallarna /hello och /{message}:

  • Båda matchar URL-sökvägen /hello.
  • /hello är mer specifik och därför högre prioritet.

I allmänhet gör routningsprioriteten ett bra jobb med att välja den bästa matchningen för de typer av URL-scheman som används i praktiken. Använd Order endast när det behövs för att undvika tvetydigheter.

På grund av de typer av utökningsbarhet som tillhandahålls av routning är det inte möjligt för routningssystemet att beräkna de tvetydiga vägarna i förväg. Överväg ett exempel som routningsmallarna /{message:alpha} och /{message:int}:

  • Villkoret alpha matchar endast alfabetiska tecken.
  • Villkoret int matchar endast tal.
  • Dessa mallar har samma vägprioritet, men det finns ingen enskild URL som båda matchar.
  • Om routningssystemet rapporterade ett tvetydighetsfel vid starten skulle det blockera det här giltiga användningsfallet.

Warning

Ordningen på åtgärderna i UseEndpoints påverkar inte beteendet för routning, med ett undantag. MapControllerRoute och MapAreaRoute tilldela automatiskt ett ordervärde till sina slutpunkter baserat på den ordning som de anropas. Detta simulerar långvarigt beteende för kontrollanter utan att routningssystemet ger samma garantier som äldre routningsimplementeringar.

Slutpunktsroutning i ASP.NET Core:

  • Har inte begreppet rutter.
  • Ger inte ordergarantier. Alla slutpunkter bearbetas samtidigt.

Routningsmallens prioritet och slutpunktsmarkeringsordning

Prioritet för routningsmallar är ett system som tilldelar varje vägmall ett värde baserat på hur specifik den är. Prioritet för routningsmall:

  • Undviker behovet av att justera ordningen på slutpunkter i vanliga fall.
  • Försöker matcha allmänna förväntningar på routingbeteende.

Du kan till exempel överväga mallar /Products/List och /Products/{id}. Det är rimligt att anta att det /Products/List är en bättre matchning än /Products/{id} för URL-sökvägen /Products/List. Detta fungerar eftersom literalsegmentet /List anses ha bättre prioritet än parametersegmentet /{id}.

Information om hur prioritet fungerar är kopplat till hur routningsmallar definieras:

  • Mallar med fler segment anses vara mer specifika.
  • Ett segment med literaltext anses vara mer specifikt än ett parametersegment.
  • Ett parametersegment med en begränsning anses vara mer specifikt än ett utan.
  • Ett komplext segment anses vara lika specifikt som ett parametersegment med en begränsning.
  • Catch-all-parametrar är de minst specifika. Se catch-all i avsnittet Routningsmallar för viktig information om catch-all-vägar.

Begrepp för URL-generering

URL-generering:

  • Är den process genom vilken routning kan skapa en URL-sökväg baserat på en uppsättning vägvärden.
  • Tillåter en logisk separation mellan slutpunkter och URL:er som har åtkomst till dem.

Slutpunktsroutning innehåller API:et LinkGenerator . LinkGenerator är en singleton-tjänst som är tillgänglig från DI. API LinkGenerator kan användas utanför kontexten för en exekverande begäran. Mvc.IUrlHelper och scenarier som förlitar sig på IUrlHelper, till exempel Tag Helpers, HTML Helpers och Action Results, använder API:et LinkGenerator internt för att tillhandahålla funktioner för länkgenerering.

Länkgeneratorn backas upp av begreppet adress - och adressscheman. Ett adressschema är ett sätt att fastställa de slutpunkter som ska beaktas för länkgenerering. Till exempel implementeras scenarier med routningnamn och routvärden som många användare är bekanta med från kontroller och Razor sidor såsom ett adressschema.

Länkgeneratorn kan länka till styrenheter och Razor sidor via följande tilläggsmetoder:

Överbelastningar av dessa metoder tar emot argument som inkluderar HttpContext. Dessa metoder är funktionellt likvärdiga med Url.Action och Url.Page, men ger ytterligare flexibilitet och alternativ.

Metoderna GetPath* liknar Url.Action mest och Url.Page, eftersom de genererar en URI som innehåller en absolut sökväg. Metoderna GetUri* genererar alltid en absolut URI som innehåller ett schema och en värd. De metoder som accepterar en HttpContext genererar en URI i kontexten för den körande begäran. Värden ambienta för väg, URL-bassökväg, protokoll och värd från den körande begäran används om de inte åsidosätts.

LinkGenerator anropas med en adress. Generera en URI sker i två steg:

  1. En adress är bunden till en lista över slutpunkter som matchar adressen.
  2. Varje slutpunkts RoutePattern utvärderas tills ett vägmönster som matchar de angivna värdena hittas. Resultatet kombineras med de andra URI-delarna som levereras till länkgeneratorn och returneras.

Metoderna som tillhandahålls av LinkGenerator stöder standardfunktioner för länkgenerering för alla typer av adresser. Det enklaste sättet att använda länkgeneratorn är genom tilläggsmetoder som utför åtgärder för en viss adresstyp:

Tilläggsmetod Description
GetPathByAddress Genererar en URI med en absolut sökväg baserat på de angivna värdena.
GetUriByAddress Genererar en absolut URI baserat på de angivna värdena.

Warning

Var uppmärksam på följande konsekvenser av att anropa LinkGenerator metoder:

  • Använd GetUri* tilläggsmetoder med försiktighet i en appkonfiguration som inte validerar headern för inkommande begäranden. Host Om rubriken för inkommande begäranden inte verifieras kan ej betrodda begärandeindata skickas tillbaka till klienten i URI:er i en vy eller sida. Vi rekommenderar att alla produktionsappar konfigurerar sin server för att kontrollera Host headern mot kända giltiga värden.

  • Använd LinkGenerator med försiktighet i mellanprogram i kombination med Map eller MapWhen. Map* ändrar basvägen för den körande begäran, vilket påverkar utdata från länkgenereringen. LinkGenerator Alla API:er tillåter att du anger en bassökväg. Ange en tom bassökväg för att ta bort Map* effekten på länkgenereringen.

Exempel på mellanprogram

I följande exempel använder ett mellanprogram API:et LinkGenerator för att skapa en länk till en åtgärdsmetod som visar lagerprodukter. Använda länkgeneratorn genom att mata in den i en klass och anrop GenerateLink är tillgängligt för alla klasser i en app:

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

Routningsmallar

Token inom {} definierar ruttparametrar som är bundna om rutten matchas. Mer än en vägparameter kan definieras i ett vägsegment, men vägparametrarna måste avgränsas med ett literalvärde. Till exempel:

{controller=Home}{action=Index}

är inte en giltig väg eftersom det inte finns något literalvärde mellan {controller} och {action}. Routningsparametrarna måste ha ett namn och kan ha ytterligare attribut angivna.

Annan literaltext än vägparametrar (till exempel {id}) och sökvägsavgränsaren / måste matcha texten i URL:en. Textmatchning är skiftlägesokänslig och baseras på den avkodade representationen av URL:ens sökväg. Om du vill matcha en literalvägsparameter avgränsare { eller }kan du undvika avgränsare genom att upprepa tecknet. Till exempel {{ eller }}.

Asterisk * eller dubbel asterisk **:

  • Kan användas som ett prefix till en vägparameter för att binda till resten av URI:n.
  • Kallas för catch-all-parametrar. Till exempel blog/{**slug}:
    • Matchar alla URI:er som börjar med blog/ och har ett värde som följer den.
    • Värdet som följer efter blog/ tilldelas som värde för slug-routen.

Warning

En parameter kan matcha vägar felaktigt på grund av en bugg i routning. Appar som påverkas av den här buggen har följande egenskaper:

  • En allomfattande rutt, till exempel {**slug}"
  • Catch-all-vägen matchar inte begäranden som den ska matcha.
  • Om du tar bort andra rutter börjar den allomfattande rutten att fungera.

Se GitHub-buggar 18677 och 16579 till exempel fall som drabbat den här buggen.

En anmälningskorrigering för den här buggen finns i .NET Core 3.1.301 eller senare SDK. Följande kod anger en intern växel som åtgärdar felet:

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

Catch-all-parametrar kan även matcha den tomma strängen.

Parametern catch-all undflyr lämpliga tecken när vägen används för att generera en URL, inklusive sökvägsavgränsningstecken / . Till exempel genererar rutten foo/{*path} med ruttvärden { path = "my/path" }foo/my%2Fpath. Observera det undantagna snedstrecket. Använd routningsparameterprefixet ** för att avgränsa sökvägens avgränsningstecken. Vägen foo/{**path} med { path = "my/path" } genererar foo/my/path.

URL-mönster som försöker avbilda ett filnamn med ett valfritt filnamnstillägg har ytterligare överväganden. Överväg till exempel mallen files/{filename}.{ext?}. När värden för båda filename och ext finns fylls båda värdena i. Om det bara finns ett värde för filename i URL:en matchar vägen eftersom avslutandet . är valfritt. Följande URL:er matchar den här vägen:

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

Routningsparametrar kan ha standardvärden som anges genom att ange standardvärdet efter parameternamnet avgränsat med ett likhetstecken (=). Definierar {controller=Home}Home till exempel som standardvärde för controller. Standardvärdet används om inget värde finns i URL:en för parametern. Routningsparametrar görs valfria genom att ett frågetecken (?) läggs till i slutet av parameternamnet. Till exempel id?. Skillnaden mellan valfria värden och standardvägsparametrar är:

  • En routningsparameter med ett standardvärde genererar alltid ett värde.
  • En valfri parameter har endast ett värde när ett värde tillhandahålls av begärande-URL:en.

Ruttparametrarna kan ha begränsningar som måste matcha ruttvärdet som är bundet från URL:en. Att lägga till : och villkorsnamn efter namnet på routningsparametern anger en infogad begränsning för en routningsparameter. Om villkoret kräver argument omges de av parenteser (...) efter villkorsnamnet. Du kan ange flera inline-begränsningar genom att lägga till en annan : och ett annat begränsningsnamn.

Villkorsnamnet och argumenten IInlineConstraintResolver skickas till tjänsten för att skapa en instans av IRouteConstraint som ska användas i URL-bearbetning. Routningsmallen blog/{article:minlength(10)} anger till exempel en minlength begränsning med argumentet 10. Mer information om routningsbegränsningar och en lista över de begränsningar som tillhandahålls av ramverket finns i avsnittet Routningsbegränsningar .

Routningsparametrar kan också ha parametertransformatorer. Parametertransformatorer transformerar en parameters värde vid generering av länkar och matchande åtgärder och sidor till URL:er. Precis som begränsningar kan parametertransformatorer läggas till direkt i en routparameter genom att lägga till ett : och ett transformeringsnamn efter routparameternamnet. Routningsmallen blog/{article:slugify} anger till exempel en slugify transformerare. Mer information om parametertransformatorer finns i avsnittet Parametertransformatorer .

I följande tabell visas exempel på routningsmallar och deras beteende:

Routningsmall Exempel på matchande URI Förfrågan-URI:n...
hello /hello Matchar endast den ensamma sökvägen /hello.
{Page=Home} / Matchar och anger Page till Home.
{Page=Home} /Contact Matchar och anger Page till Contact.
{controller}/{action}/{id?} /Products/List Mappar till kontroller Products och åtgärd List.
{controller}/{action}/{id?} /Products/Details/123 Mappar till kontrollanten Products och Details åtgärden medid värdet 123.
{controller=Home}/{action=Index}/{id?} / Mappar till Home-kontrollern och Index-metoden. id ignoreras.
{controller=Home}/{action=Index}/{id?} /Products Mappar till Products-kontrollern och Index-metoden. id ignoreras.

Att använda en mall är vanligtvis den enklaste metoden för routning. Begränsningar och standardvärden kan också anges utanför vägmallen.

Komplexa segment

Komplexa segment bearbetas genom matchning av literalavgränsare från höger till vänster på ett icke-girigt sätt. Till exempel [Route("/a{b}c{d}")] är ett komplext segment. Komplexa segment fungerar på ett visst sätt som måste tolkas för att kunna använda dem. Exemplet i det här avsnittet visar varför komplexa segment bara fungerar bra när avgränsartexten inte visas i parametervärdena. Att använda en regex och sedan extrahera värdena manuellt behövs för mer komplexa fall.

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Det här är en sammanfattning av de steg som routningen utför med mallen /a{b}c{d} och URL-sökvägen /abcd. | Används för att visualisera hur algoritmen fungerar:

  • Den första literalen, från höger till vänster, är c. Så /abcd söks från höger och hittar /ab|c|d.
  • Allt till höger (d) matchas nu med routningsparametern {d}.
  • Nästa literal, från höger till vänster, är a. Så /ab|c|d genomsöks från där vi slutade, sedan hittas a/|a|b|c|d.
  • Värdet till höger (b) matchas nu med routningsparametern {b}.
  • Det finns ingen kvarvarande text och ingen kvarvarande mall för rutt, så det här är en matchning.

Här är ett exempel på ett negativt ärende med samma mall /a{b}c{d} och URL-sökvägen /aabcd. | Används för att visualisera hur algoritmen fungerar. Det här fallet är inte en matchning, vilket förklaras av samma algoritm:

  • Den första literalen, från höger till vänster, är c. Så /aabcd söks från höger och hittar /aab|c|d.
  • Allt till höger (d) matchas nu med routningsparametern {d}.
  • Nästa literal, från höger till vänster, är a. Så /aab|c|d genomsöks från där vi slutade, sedan hittas a/a|a|b|c|d.
  • Värdet till höger (b) matchas nu med routningsparametern {b}.
  • I det här läget finns det fortfarande text a, men algoritmen har slut på vägmall för analys, så detta stämmer inte.

Eftersom matchningsalgoritmen inte är girig:

  • Den matchar den minsta möjliga mängden text i varje steg.
  • Alla fall där avgränsarvärdet visas i parametervärdena resulterar i att det inte matchar.

Reguljära uttryck ger mycket mer kontroll över deras matchande beteende.

Girig matchning, också kallad försiktig matchning, matchar den största möjliga strängen. Icke-giriga matchar den minsta möjliga strängen.

Routning med specialtecken

Routning med specialtecken kan leda till oväntade resultat. Tänk dig till exempel en kontrollant med följande åtgärdsmetod:

[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;
}

När string id innehåller följande kodade värden kan oväntade resultat inträffa:

ASCII Encoded
/ %2F
+

Routningsparametrar är inte alltid URL-avkodade. Det här problemet kan åtgärdas i framtiden. Mer information finns i det här GitHub-problemet;

Vägbegränsningar

Routningsbegränsningar körs när en matchning har hittats för den inkommande URL:en och URL-sökvägen tokeniseras till ruttvärden. Routningsbegränsningar inspekterar vanligtvis det vägvärde som är associerat via routningsmallen och fattar ett sant eller falskt beslut om huruvida värdet är acceptabelt. Vissa routningsbegränsningar använder data utanför vägvärdet för att överväga om begäran kan dirigeras. Till exempel HttpMethodRouteConstraint kan acceptera eller avvisa en begäran baserat på dess HTTP-verb. Begränsningar används i routningsbegäranden och länkgenerering.

Warning

Använd inte begränsningar för indataverifiering. Om begränsningar används för indataverifiering resulterar ogiltiga indata i ett 404 svar som inte hittades. Ogiltiga indata bör generera en 400 felaktig begäran med ett lämpligt felmeddelande. Routningsbegränsningar används för att särskilja liknande rutter, inte för att verifiera inmatningar för en viss rutt.

I följande tabell visas exempel på vägbegränsningar och deras förväntade beteende:

constraint Example Exempelmatchningar Notes
int {id:int} 123456789, -123456789 Matchar alla heltal
bool {active:bool} true, FALSE Matchar true eller false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Matchar ett giltigt DateTime värde i den invarianta kulturen. Se föregående varning.
decimal {price:decimal} 49.99, -1,000.01 Matchar ett giltigt decimal värde i den invarianta kulturen. Se föregående varning.
double {weight:double} 1.234, -1,001.01e8 Matchar ett giltigt double värde i den invarianta kulturen. Se föregående varning.
float {weight:float} 1.234, -1,001.01e8 Matchar ett giltigt float värde i den invarianta kulturen. Se föregående varning.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Matchar ett giltigt Guid värde
long {ticks:long} 123456789, -123456789 Matchar ett giltigt long värde
minlength(value) {username:minlength(4)} Rick Strängen måste innehålla minst 4 tecken
maxlength(value) {filename:maxlength(8)} MyFile Strängen får inte innehålla fler än 8 tecken
length(length) {filename:length(12)} somefile.txt Strängen måste vara exakt 12 tecken lång
length(min,max) {filename:length(8,16)} somefile.txt Strängen får vara minst 8 och högst 16 tecken lång
min(value) {age:min(18)} 19 Heltalsvärdet måste vara minst 18
max(value) {age:max(120)} 91 Heltalsvärdet får inte vara mer än 120
range(min,max) {age:range(18,120)} 91 Heltalsvärdet måste vara minst 18 men högst 120
alpha {name:alpha} Rick Strängen måste bestå av ett eller flera alfabetiska tecken, och vara skiftlägesokänslig, a-z.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 Strängen måste matcha det reguljära uttrycket. Se tips om hur du definierar ett reguljärt uttryck.
required {name:required} Rick Används för att framtvinga att ett icke-parametervärde finns under URL-generering

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Flera, kolonavgränsade begränsningar kan tillämpas på en enskild parameter. Följande villkor begränsar till exempel en parameter till ett heltalsvärde på 1 eller högre:

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

Warning

Routningsbegränsningar som verifierar URL:en och konverteras till en CLR-typ använder alltid den invarianta kulturen. Till exempel konvertering till CLR-typen int eller DateTime. Dessa begränsningar förutsätter att URL:en inte kan lokaliseras. De ramverksbaserade vägbegränsningarna ändrar inte de värden som lagras i vägvärden. Alla vägvärden som parsas från URL:en lagras som strängar. Villkoret float försöker till exempel konvertera vägvärdet till en flyttal, men det konverterade värdet används bara för att verifiera att det kan konverteras till en flyttal.

Reguljära uttryck i begränsningar

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Reguljära uttryck kan anges som infogade begränsningar med hjälp av routningsbegränsningen regex(...) . Metoder i MapControllerRoute familjen accepterar också en objektliteral av begränsningar. Om formuläret används tolkas strängvärden som reguljära uttryck.

Följande kod använder en infogad regex-begränsning:

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

Följande kod använder en objektliteral för att ange en regex-begränsning:

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

Ramverket ASP.NET Core lägger RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant till konstruktorn för reguljära uttryck. Se RegexOptions en beskrivning av dessa medlemmar.

Reguljära uttryck använder avgränsare och token som liknar dem som används vid routning och C#-språket. Token för reguljära uttryck måste vara undantagna. Om du vill använda reguljära uttryck ^\d{3}-\d{2}-\d{4}$ i en infogad begränsning använder du något av följande:

  • Ersätt \ tecken som anges i strängen som \\ tecken i C#-källfilen för att undvika strängens \ escape-tecken.
  • Verbatim strängliteraler.

Om du vill undvika dirigeringsparametertecken {, }, [, ], dubblar du tecknen i uttrycket, {{till exempel , }}, [[, . ]] I följande tabell visas ett reguljärt uttryck och dess undantagna version:

Reguljärt uttryck Undantagna reguljära uttryck
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Reguljära uttryck som används i routning börjar ofta med ^ tecknet och matchar strängens startposition. Uttrycken slutar ofta med $ tecknet och matchar slutet av strängen. Tecknen ^ och $ ser till att det reguljära uttrycket matchar värdet för hela routningsparametern. Utan ^- och $-tecknen matchar det reguljära uttrycket vilken delsträng som helst i strängen, vilket ofta är oönskat. Följande tabell innehåller exempel och förklarar varför de matchar eller inte matchar:

Expression String Match Comment
[a-z]{2} hello Yes Matchningar för delsträngar
[a-z]{2} 123abc456 Yes Matchningar för delsträngar
[a-z]{2} mz Yes Matchar uttryck
[a-z]{2} MZ Yes Inte skiftlägeskänslig
^[a-z]{2}$ hello No Se ^ och $ ovan
^[a-z]{2}$ 123abc456 No Se ^ och $ ovan

Mer information om syntax för reguljära uttryck finns i Reguljära .NET Framework-uttryck.

Om du vill begränsa en parameter till en känd uppsättning möjliga värden använder du ett reguljärt uttryck. Till exempel {action:regex(^(list|get|create)$)} matchar endast routningsvärdet action till list, geteller create. Om den skickas in i begränsningsordboken, så är strängen ^(list|get|create)$ likvärdig. Begränsningar som skickas i villkorsordlistan som inte matchar någon av de kända begränsningarna behandlas också som reguljära uttryck. Begränsningar som skickas i en mall som inte matchar någon av de kända begränsningarna behandlas inte som reguljära uttryck.

Begränsningar för anpassad rutt

Anpassade routningsbegränsningar kan skapas genom att implementera IRouteConstraint gränssnittet. Gränssnittet IRouteConstraint innehåller Match, som returnerar true om villkoret är uppfyllt och false på annat sätt.

Anpassade routningsbegränsningar behövs sällan. Innan du implementerar en anpassad vägbegränsning bör du överväga alternativ, till exempel modellbindning.

Mappen ASP.NET Core Constraints innehåller bra exempel på hur du skapar begränsningar. Till exempel GuidRouteConstraint.

Om du vill använda en anpassad IRouteConstraintmåste routningsbegränsningstypen registreras med appens ConstraintMap i tjänstcontainern. A ConstraintMap är en ordlista som mappar routningsbegränsningsnycklar till IRouteConstraint implementeringar som validerar dessa begränsningar. En app"s ConstraintMap kan uppdateras i Program.cs antingen som en del av ett AddRouting-anrop eller genom att konfigurera RouteOptions direkt med builder.Services.Configure<RouteOptions>. Till exempel:

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

Föregående villkor tillämpas i följande kod:

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

Implementeringen av NoZeroesRouteConstraint förhindrar 0 att den används i en vägparameter:

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

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Föregående kod:

  • Förhindrar 0 i {id} segmentet av rutten.
  • Visas som ett grundläggande exempel på hur du implementerar en anpassad begränsning. Den bör inte användas i en produktionsapp.

Följande kod är en bättre metod för att förhindra att en id innehållande en 0 bearbetas:

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

    return Content(id);
}

Föregående kod har följande fördelar jämfört med NoZeroesRouteConstraint metoden:

  • Det kräver ingen anpassad begränsning.
  • Det returnerar ett mer beskrivande fel när vägparametern innehåller 0.

Parametertransformatorer

Parametertransformatorer:

Till exempel, en anpassad slugify-parametertransformator i vägmönster blog\{article:slugify} med Url.Action(new { article = "MyTestArticle" }) genererar blog\my-test-article.

Överväg följande IOutboundParameterTransformer implementering:

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

Om du vill använda en parametertransformator i ett vägmönster konfigurerar du den med hjälp av ConstraintMap i Program.cs:

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

ASP.NET Core-ramverket använder parametertransformatorer för att transformera den URI där en slutpunkt löser problemet. Till exempel transformerar parametertransformatorer de vägvärden som används för att matcha en area, controller, actionoch page:

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

Med föregående vägmall matchas åtgärden SubscriptionManagementController.GetAll med URI /subscription-management/get-all:n . En parametertransformator ändrar inte de vägvärden som används för att generera en länk. Till exempel Url.Action("GetAll", "SubscriptionManagement") ger /subscription-management/get-all.

ASP.NET Core tillhandahåller API-konventioner för användning av parametertransformatorer med genererade vägar:

Referens för URL-generering

Det här avsnittet innehåller en referens för algoritmen som implementeras av URL-generering. I praktiken använder de flesta komplexa exempel på URL-generering styrenheter eller Razor sidor. Mer information finns i routning i kontrollanter .

URL-genereringsprocessen börjar med ett anrop till LinkGenerator.GetPathByAddress eller en liknande metod. Metoden tillhandahålls med en adress, en uppsättning vägvärden och eventuellt information om den aktuella begäran från HttpContext.

Det första steget är att använda adressen för att lösa en uppsättning kandidatslutpunkter med hjälp av en IEndpointAddressScheme<TAddress> som matchar adressens typ.

När uppsättningen kandidater hittas av adressschemat sorteras slutpunkterna och bearbetas iterativt tills en URL-genereringsåtgärd lyckas. URL-genereringen söker inte efter tvetydigheter. Det första resultatet som returneras är slutresultatet.

Felsöka URL-generering med loggning

Det första steget i felsökning av URL-generering för Microsoft.AspNetCore.Routing är att ställa in loggningsnivån till TRACE. LinkGenerator loggar många detaljer om bearbetningen som kan vara användbar för att felsöka problem.

Mer information om URL-generering finns i REFERENS för URL-generering .

Addresses

Adresser är det begrepp i URL-generering som används för att binda ett anrop till länkgeneratorn till en uppsättning kandidatslutpunkter.

Adresser är ett utökningsbart begrepp som levereras med två implementeringar som standard:

  • Använda slutpunktsnamnet (string) som adress:
    • Tillhandahåller liknande funktioner som MVC:s routningsnamn.
    • IEndpointNameMetadata Använder metadatatypen.
    • Löser den angivna strängen mot metadata för alla registrerade slutpunkter.
    • Utlöser ett undantag vid start om flera slutpunkter använder samma namn.
    • Rekommenderas för generell användning utanför kontrollanter och Razor sidor.
  • Använda vägvärden (RouteValuesAddress) som adress:
    • Ger liknande funktioner som kontroller och äldre URL-generering för sidor.
    • Mycket komplext att utöka och felsöka.
    • Tillhandahåller implementeringen som används av IUrlHelper, Tag Helpers, HTML-hjälpare, åtgärdsresultat osv.

Adressschemats roll är att göra associationen mellan adressen och matchande slutpunkter enligt godtyckliga kriterier:

  • Slutpunktsnamnschemat utför en grundläggande ordlistesökning.
  • Routningsvärdena har en komplex bästa delmängd av set-algoritmen.

Omgivande värden och explicita värden

Från den aktuella begäran får routning åtkomst till routningsvärdena för den aktuella begäran HttpContext.Request.RouteValues. De värden som är associerade med den aktuella begäran kallas för omgivande värden. För tydlighetens skull refererar dokumentationen till de vägvärden som skickas in till metoder som explicita värden.

I följande exempel visas omgivande värden och explicita värden. Den innehåller omgivande värden från den aktuella begäran och explicita värden:

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

    // ...

Föregående kod:

Följande kod innehåller endast explicita värden och inga omgivande värden:

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

Föregående metod returnerar /Home/Subscribe/17

Följande kod i WidgetController returnerar /Widget/Subscribe/17:

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

Följande kod tillhandahåller kontrollern från omgivande och explicita värden i den aktuella begäran.

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

I koden ovan:

  • /Gadget/Edit/17 returneras.
  • Url hämtar IUrlHelper.
  • Action genererar en URL med en absolut sökväg för en åtgärdsmetod. URL:en innehåller det angivna action namnet och route värdena.

Följande kod innehåller omgivande värden från den aktuella begäran och explicita värden:

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

        // ...
    }
}

Den föregående koden sätter url till /Edit/17 när Redigera Razor Sida innehåller följande siddirektiv:

@page "{id:int}"

Om sidan Redigera inte innehåller "{id:int}"-routemallen, är url/Edit?id=17.

Beteendet för MVC lägger IUrlHelper till ett lager av komplexitet utöver de regler som beskrivs här:

  • IUrlHelper tillhandahåller alltid vägvärdena från den aktuella begäran som omgivande värden.
  • IUrlHelper.Action kopierar alltid aktuella action värden och controller vägvärden som explicita värden om de inte åsidosätts av utvecklaren.
  • IUrlHelper.Page kopierar alltid det aktuella page vägvärdet som ett explicit värde om det inte åsidosätts.
  • IUrlHelper.Page åsidosätter alltid det aktuella handler ruttvärdet med null som ett explicit värde såvida det inte åsidosätts.

Användarna blir ofta förvånade över beteendeinformationen för omgivande värden, eftersom MVC inte verkar följa sina egna regler. Av historiska och kompatibilitetsskäl har vissa routningsvärden som action, controller, pageoch handler ett eget specialfallsbeteende.

Motsvarande funktionalitet som tillhandahålls av LinkGenerator.GetPathByAction och LinkGenerator.GetPathByPage duplicerar dessa avvikelser i IUrlHelper för kompatibilitet.

Process för URL-generering

När uppsättningen kandidatslutpunkter hittas, algoritmen för URL-generering:

  • Bearbetar slutpunkterna iterativt.
  • Returnerar det första lyckade resultatet.

Det första steget i den här processen kallas ogiltigförklarande av routningsvärden. Ogiltigförklaring av routningsvärden är den process genom vilken routning avgör vilka routningsvärden från bakgrundsvärden som ska användas och vilka som ska ignoreras. Varje omgivande värde beaktas och kombineras antingen med explicita värden eller ignoreras.

Det bästa sättet att förstå rollen av omgivande värden är att de försöker spara skrivande för programutvecklare i vissa vanliga situationer. Traditionellt är scenarier där omgivande värden är användbara relaterade till MVC:

  • När du länkar till en annan åtgärd i samma kontrollant behöver inte kontrollantnamnet anges.
  • När du länkar till en annan kontrollant i samma område behöver du inte ange områdesnamnet.
  • När du länkar till samma åtgärdsmetod behöver du inte ange vägvärden.
  • När du länkar till en annan del av appen vill du inte överföra routningsvärden som inte har någon betydelse i den delen av appen.

Anrop till LinkGenerator eller IUrlHelper som returnerar null orsakas vanligtvis av bristande förståelse av ogiltigförklaring av routningsvärden. Felsöka routningsvärdets ogiltighet genom att uttryckligen ange fler av vägvärdena för att se om det löser problemet.

Ogiltigförklaring av routvärden bygger på antagandet att appens URL-schema är hierarkiskt, med en hierarki som bildas från vänster till höger. Överväg den grundläggande mallen för styrenhetsroutning {controller}/{action}/{id?} för att få en intuitiv uppfattning om hur detta fungerar i praktiken. En ändring av ett värde ogiltigförklarar alla vägvärden som visas till höger. Detta återspeglar antagandet om hierarki. Om appen har ett omgivande värde för id, och åtgärden anger ett annat värde för controller:

  • id återanvänds inte eftersom {controller} är till vänster om {id?}.

Några exempel som visar den här principen:

  • Om de explicita värdena innehåller ett värde för idignoreras det omgivande värdet för id . Omgivande värden för controller och action kan användas.
  • Om de explicita värdena innehåller ett värde för actionignoreras alla omgivande värden för action . Omgivningsvärdena för controller kan användas. Om det explicita värdet för action skiljer sig från det omgivande värdet för actionid används inte värdet. Om det tydliga värdet för action är detsamma som det omgivande värdet för action, kan id värdet användas.
  • Om de explicita värdena innehåller ett värde för controllerignoreras alla omgivande värden för controller . Om det explicita värdet för controller skiljer sig från det omgivande värdet för controlleraction används inte värdena ochid. Om det explicita värdet för controller är detsamma som det omgivande värdet för controlleraction kan värdena och id användas.

Den här processen kompliceras ytterligare av förekomsten av attributvägar och dedikerade konventionella vägar. Styrenheters konventionella vägar som {controller}/{action}/{id?} specificerar en hierarki med hjälp av ruttparametrar. För dedikerade konventionella vägar och attributvägar till styrningar och Razor sidor:

  • Det finns en hierarki med vägvärden.
  • De visas inte i mallen.

I dessa fall definierar URL-generering det nödvändiga värdekonceptet . Slutpunkter som skapats av kontrollanter och Razor sidor har angivna värden som gör att vägvärdets ogiltighet kan fungera.

Ruttvärdets ogiltighetsalgoritm förklarad:

  • De nödvändiga värdenamnen kombineras med vägparametrarna och bearbetas sedan från vänster till höger.
  • För varje parameter jämförs det omgivande värdet och det explicita värdet:
    • Om det omgivande värdet och det explicita värdet är samma fortsätter processen.
    • Om det omgivande värdet finns och det explicita värdet inte är det används det omgivande värdet när URL:en genereras.
    • Om det omgivande värdet inte finns och det explicita värdet är avvisar du det omgivande värdet och alla efterföljande omgivande värden.
    • Om det omgivande värdet och det explicita värdet finns, och de två värdena är olika, avvisar du det omgivande värdet och alla efterföljande omgivande värden.

I det här läget är URL-genereringsåtgärden redo att utvärdera routningsbegränsningar. Uppsättningen godkända värden kombineras med parameterns standardvärden, vilka tillhandahålls för begränsningar. Om alla begränsningar uppfylls fortsätter operationen.

Därefter kan de godkända värdena användas för att expandera routningsmallen. Vägmallen bearbetas:

  • Från vänster till höger.
  • Varje parameter har sitt godkända värde ersatt.
  • Med följande specialfall:
    • Om de godkända värdena saknar ett värde och parametern har ett standardvärde används standardvärdet.
    • Om de godkända värdena saknar ett värde och parametern är valfri fortsätter bearbetningen.
    • Om en routningsparameter till höger om en valfri parameter som saknas har ett värde misslyckas åtgärden.
    • Sammanhängande standardvärdeparametrar och valfria parametrar komprimeras där det är möjligt.

Värden som uttryckligen anges som inte matchar ett segment av vägen läggs till i frågesträngen. Följande tabell visar resultatet när du använder routningsmallen {controller}/{action}/{id?}.

Omgivande värden Explicita värden Result
controller = "Home" action = "Om" /Home/About
controller = "Home" controller = "Beställning", action = "Om" /Order/About
controller = "Home", color = "Röd" action = "Om" /Home/About
controller = "Home" action = "Om", color = "Röd" /Home/About?color=Red

Problem med att routningsvärdet är ogiltigt

Följande kod visar ett exempel på ett URL-genereringsschema som inte stöds av routning:

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

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

I föregående kod culture används routningsparametern för lokalisering. Önskan är att parametern culture alltid ska accepteras som ett omgivande värde. Parametern culture accepteras dock inte som ett omgivande värde på grund av hur nödvändiga värden fungerar:

  • I routningsmallen "default" är routningsparametern culture till vänster om controller, så ändringar i kommer inte att controller ogiltigförklara culture.
  • "blog" I vägmallen culture anses vägparametern vara till höger om controller, som visas i de obligatoriska värdena.

Parsa URL-sökvägar med LinkParser

Klassen LinkParser lägger till stöd för att parsa en URL-sökväg till en uppsättning vägvärden. Metoden ParsePathByEndpointName tar ett slutpunktsnamn och en URL-sökväg och returnerar en uppsättning vägvärden som extraherats från URL-sökvägen.

I följande exempelkontrollant använder åtgärden GetProduct en routningsmall med api/Products/{id} och har en Name av GetProduct:

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

I samma kontrollantklass AddRelatedProduct förväntar sig åtgärden en URL-sökväg, , pathToRelatedProductsom kan anges som en frågesträngsparameter:

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

    // ...

I föregående exempel extraherar AddRelatedProduct åtgärden id ruttvärdet från URL-sökvägen. Till exempel, med en URL-sökväg på /api/Products/1, är värdet på relatedProductId inställt till 1. Med den här metoden kan API:ets klienter använda URL-sökvägar när de refererar till resurser, utan att behöva veta hur en sådan URL är strukturerad.

Konfigurera slutpunktsmetadata

Följande länkar innehåller information om hur du konfigurerar slutpunktsmetadata:

Värdmatchning i rutter med RequireHost

RequireHost tillämpar en begränsning på den rutt som kräver den angivna värden. Parametern RequireHost eller [Värd] kan vara en:

  • Värd: www.domain.com, matchar www.domain.com med valfri port.
  • Värd med jokertecken: *.domain.com, matchar www.domain.com, subdomain.domain.com, eller www.subdomain.domain.com på valfri port.
  • Port: *:5000, matchar port 5000 med valfri värd.
  • Värd och port: www.domain.com:5000 eller *.domain.com:5000, matchar värd och port.

Flera parametrar kan anges med RequireHost eller [Host]. Villkoret matchar värdar som är giltiga för någon av parametrarna. Matchar till exempel [Host("domain.com", "*.domain.com")], domain.com, www.domain.com, och subdomain.domain.com.

Följande kod använder RequireHost för att kräva den angivna värden på rutten:

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

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

Följande kod använder [Host] attributet på kontrollanten för att kräva någon av de angivna värdarna:

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

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

[Host] När attributet tillämpas på både kontrollanten och åtgärdsmetoden:

  • Attributet för åtgärden används.
  • Kontrollantattributet ignoreras.

Prestandavägledning för routning

När en app har prestandaproblem misstänks routning ofta vara problemet. Anledningen till att routning misstänks är att ramverk som kontrollers och Razor Pages rapporterar hur lång tid som spenderas inom ramverket i sina loggningsmeddelanden. När det finns en betydande skillnad mellan den tid som rapporteras av kontrollanter och den totala tiden för begäran:

  • Utvecklare eliminerar sin appkod som källa till problemet.
  • Det är vanligt att anta att routning är orsaken.

Routning är prestandatestad med tusentals slutpunkter. Det är osannolikt att en typisk app kommer att stöta på ett prestandaproblem bara genom att vara för stor. Den vanligaste grundorsaken till långsamma routningsprestanda är vanligtvis ett dåligt fungerande anpassad middleware.

Följande kodexempel visar en grundläggande teknik för att begränsa fördröjningskällan:

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

För att schemalägga dirigeringen:

  • Alternativt infoga mellanprogrammen med en kopia av tidsmellanprogrammet som visas i föregående kod.
  • Lägg till en unik identifierare för att korrelera tidsdata med koden.

Det här är ett grundläggande sätt att begränsa fördröjningen när den är betydande, till exempel mer än 10ms. Subtrahera Time 2 från Time 1 rapporterar tiden som spenderats i UseRouting mellanprogrammet.

Följande kod använder en mer kompakt metod för föregående tidskod:

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

Potentiellt dyra routningsfunktioner

Följande lista ger en inblick i routningsfunktioner som är relativt dyra jämfört med grundläggande routningsmallar:

  • Reguljära uttryck: Det går att skriva reguljära uttryck som är komplexa eller har lång körningstid med en liten mängd indata.
  • Komplicerade segment ({x}-{y}-{z}):
    • Är betydligt dyrare än att parsa ett vanligt URL-sökvägssegment.
    • Resulterar i att många fler delsträngar kommer att allokeras.
  • Synkron dataåtkomst: Många komplexa appar har databasåtkomst som en del av routningen. Använd utökningspunkter som MatcherPolicy och EndpointSelectorContext, som är asynkrona.

Vägledning för stora routningstabeller

Som standard använder ASP.NET Core en routningsalgoritm som byter minne mot CPU-tid. Detta har den fina effekten att vägmatchningstiden endast beror på längden på sökvägen som ska matchas och inte antalet vägar. Den här metoden kan dock vara potentiellt problematisk i vissa fall, när appen har ett stort antal vägar (i tusental) och det finns en hög mängd variabelprefix i vägarna. Om rutterna har parametrar i tidiga segment av rutten, som {parameter}/some/literal.

Det är osannolikt att en app stöter på en situation där detta är ett problem om inte:

  • Det finns ett stort antal vägar i appen med det här mönstret.
  • Det finns ett stort antal vägar i appen.

Så här avgör du om en app stöter på problemet med den stora routningstabellen

  • Det finns två symptom att leta efter:
    • Appen startar långsamt vid första användningen.
      • Observera att detta är obligatoriskt men inte tillräckligt. Det finns många andra problem som inte är routningsproblem än vad som kan orsaka långsam appstart. Kontrollera om villkoret nedan är korrekt för att fastställa att appen körs i den här situationen.
    • Appen förbrukar mycket minne under starten och en minnesdump visar ett stort antal Microsoft.AspNetCore.Routing.Matching.DfaNode instanser.

Så här åtgärdar du det här problemet

Det finns flera tekniker och optimeringar som kan tillämpas på vägar som till stor del förbättrar det här scenariot:

  • Tillämpa routningsbegränsningar på dina parametrar, till exempel {parameter:int}, {parameter:guid}, {parameter:regex(\\d+)}osv. där det är möjligt.
    • På så sätt kan routningsalgoritmen internt optimera de strukturer som används för matchning och drastiskt minska det minne som används.
    • I de allra flesta fall räcker detta för att återgå till ett acceptabelt beteende.
  • Ändra vägarna för att flytta parametrar till senare segment i mallen.
    • Detta minskar antalet möjliga "vägar" för att matcha en slutpunkt med en given väg.
  • Använd en dynamisk rutt och utför mappningen till en kontroller/sida dynamiskt.
    • Detta kan uppnås med hjälp av MapDynamicControllerRoute och MapDynamicPageRoute.

Vägledning för biblioteksförfattare

Det här avsnittet innehåller vägledning för biblioteksförfattare som bygger ovanpå routning. Den här informationen är avsedd att säkerställa att apputvecklare har en bra upplevelse med hjälp av bibliotek och ramverk som utökar routningen.

Definiera slutpunkter

Om du vill skapa ett ramverk som använder routning för URL-matchning börjar du med att definiera en användarupplevelse som bygger ovanpå UseEndpoints.

BYGG ovanpå IEndpointRouteBuilder. På så sätt kan användarna skapa ditt ramverk med andra ASP.NET Core-funktioner utan förväxling. Varje ASP.NET Core-mall innehåller routning. Anta att routning är närvarande och bekant för användare.

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

app.MapHealthChecks("/healthz");

Returnera en förseglad betongtyp från ett anrop till MapMyFramework(...) som implementerar IEndpointConventionBuilder. De flesta ramverksmetoder Map... följer det här mönstret. Gränssnittet IEndpointConventionBuilder :

  • Tillåter att metadata skapas.
  • Är mål för en mängd olika tilläggsmetoder.

Om du deklarerar din egen typ kan du lägga till dina egna ramverksspecifika funktioner i byggaren. Det är okej att omsluta ett ramverksdeklarerat byggobjekt och vidarebefordra anrop till det.

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

app.MapHealthChecks("/healthz");

ÖVERVÄG att skriva din egen EndpointDataSource. EndpointDataSource är en grundläggande konstruktion för att deklarera och uppdatera en samling slutpunkter. EndpointDataSource är ett kraftfullt API som används av kontrollanter och Razor sidor.

Routningstesterna har ett grundläggande exempel på en datakälla som inte uppdateras.

Försök INTE att registrera en EndpointDataSource som standard. Kräv att användare registrerar ditt ramverk i UseEndpoints. Routningsfilosofin är att ingenting ingår som standard, och det UseEndpoints är platsen där slutpunkter ska registreras.

Skapa routningsintegrerade mellanprogram

ÖVERVÄG att definiera metadatatyper som ett gränssnitt.

Gör det möjligt att använda metadatatyper som ett attribut för klasser och metoder.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

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

Ramverk som kontrollanter och Razor sidor stöder tillämpning av metadataattribut på typer och metoder. Om du deklarerar metadatatyper:

  • Gör dem tillgängliga som attribut.
  • De flesta användare är bekanta med att tillämpa attribut.

Om du deklarerar en metadatatyp som ett gränssnitt läggs ett annat flexibelt lager till:

  • Gränssnitt är sammansättningsbara.
  • Utvecklare kan deklarera sina egna typer som kombinerar flera principer.

Gör det möjligt att åsidosätta metadata, som du ser i följande exempel:

[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() { }
}

Det bästa sättet att följa dessa riktlinjer är att undvika att definiera markörmetadata:

  • Leta inte bara efter förekomsten av en metadatatyp.
  • Definiera en egenskap för metadata och kontrollera egenskapen.

Metadatasamlingen är ordnad och möjliggör att överskrida efter prioritet. När det gäller kontrollanter är metadata för åtgärdsmetoden mest specifika.

Gör mellanprogram användbara med och utan routning:

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

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

Som ett exempel på den här riktlinjen bör du överväga UseAuthorization mellanprogrammet. Med mellanprogrammet för auktorisering kan du ange en reservpolicy. Om den anges, gäller återställningsprincipen för båda:

  • Slutpunkter utan en angiven policy.
  • Begäranden som inte överensstämmer med en slutpunkt.

Detta gör auktoriseringsmellanprogrammet användbart utanför routningskontexten. Mellanprogrammet för auktorisering kan användas för traditionell mellanprogramprogrammering.

Felsökningsdiagnostik

För detaljerade routningsdiagnostikutdata anger du Logging:LogLevel:Microsoft till Debug. I utvecklingsmiljön anger du loggnivån i appsettings.Development.json:

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

Ytterligare resurser

Routning ansvarar för att matcha inkommande HTTP-begäranden och skicka dessa begäranden till appens körbara slutpunkter. Slutpunkter är appens enheter för körbar kod för hantering av begäranden. Slutpunkter definieras i appen och konfigureras när appen startas. Slutpunktsmatchningsprocessen kan extrahera värden från begärans URL och ange dessa värden för bearbetning av begäranden. Med hjälp av slutpunktsinformation från appen kan routning också generera URL:er som mappar till slutpunkter.

Appar kan konfigurera routning med hjälp av:

  • Controllers
  • Razor sidor
  • SignalR
  • gRPC-tjänster
  • Slutpunktsaktiverat mellanprogram , till exempel hälsokontroller.
  • Delegater och lambdas som registrerats med routning.

Det här dokumentet beskriver information på låg nivå om ASP.NET Core-routning. Information om hur du konfigurerar routning:

Slutpunktsroutningssystemet som beskrivs i det här dokumentet gäller för ASP.NET Core 3.0 eller senare. Om du vill ha information om det tidigare routningssystemet baserat på IRouterväljer du ASP.NET Core 2.1-versionen med någon av följande metoder:

Visa eller ladda ned exempelkod (hur du laddar ned)

Nedladdningsexemplen för det här dokumentet aktiveras av en specifik Startup klass. Om du vill köra ett specifikt exempel ändrar du Program.cs för att anropa önskad Startup klass.

Grunderna för routning

Alla ASP.NET Core-mallar inkluderar routning i den genererade koden. Routning registreras i middleware-pipelinen i Startup.Configure.

Följande kod visar ett grundläggande exempel på routning:

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

Routning använder ett par mellanprogram, registrerade av UseRouting och UseEndpoints:

  • UseRouting lägger till routningsmatchning till pipelinen för mellanprogram. Det här mellanprogrammet tittar på den uppsättning slutpunkter som definierats i appen och väljer den bästa matchningen baserat på begäran.
  • UseEndpoints lägger till slutpunktskörning i pipelinen för mellanprogram. Den kör delegeringen som är kopplad till den valda slutpunkten.

Föregående exempel innehåller en enda väg till kodslutpunkten med metoden MapGet :

  • När en HTTP-begäran GET skickas till rot-URL:en /:
    • Delegeringen för begäran som visas utförs.
    • Hello World! skrivs till HTTP-svaret. Som standard är rot-URL:en / är https://localhost:5001/.
  • Om begärandemetoden inte är GET eller om rot-URL:en inte är /, matchar ingen rutt och en HTTP 404 returneras.

Endpoint

Metoden MapGet används för att definiera en slutpunkt. En slutpunkt är något som kan vara:

  • Vald genom att matcha URL:en och HTTP-metoden.
  • Utförs genom att köra delegeringen.

Ändpunkter som kan matchas och köras av appen konfigureras i UseEndpoints. Till exempel ansluter MapGet, MapPost och liknande metoder förbinder ombud för begäranden till routningssystemet. Ytterligare metoder kan användas för att ansluta ASP.NET Core Framework-funktioner till routningssystemet:

I följande exempel visas routning med en mer avancerad vägmall:

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/hello/{name:alpha}", async context =>
    {
        var name = context.Request.RouteValues["name"];
        await context.Response.WriteAsync($"Hello {name}!");
    });
});

Strängen /hello/{name:alpha} är en vägmall. Den används för att konfigurera hur slutpunkten matchas. I det här fallet matchar mallen:

  • En URL som /hello/Ryan
  • Alla URL-sökvägar som börjar med /hello/ följt av en sekvens med alfabetiska tecken. :alpha tillämpar en vägbegränsning som endast matchar alfabetiska tecken. Routningsbegränsningar beskrivs senare i det här dokumentet.

Det andra segmentet i URL-sökvägen: {name:alpha}

Slutpunktsroutningssystemet som beskrivs i det här dokumentet är nytt från och med ASP.NET Core 3.0. Alla versioner av ASP.NET Core stöder dock samma uppsättning routningsmallsfunktioner och routningsbegränsningar.

I följande exempel visas routning med hälsokontroller och auktorisering:

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

Om du vill se kodkommentar översatta till andra språk än engelska kan du meddela oss i det här GitHub-diskussionsproblemet.

Föregående exempel visar hur:

  • Mellanprogrammet för auktorisering kan användas med routning.
  • Slutpunkter kan användas för att konfigurera auktoriseringsbeteende.

Anropet MapHealthChecks lägger till en slutpunkt för hälsokontroll. Länkning RequireAuthorization till det här anropet kopplar en auktoriseringsprincip till slutpunkten.

Anropar UseAuthentication och UseAuthorization lägger till mellanprogrammet för autentisering och auktorisering. Dessa mellanprogram placeras mellan UseRouting och UseEndpoints så att de kan:

  • Se vilken slutpunkt som har valts av UseRouting.
  • Tillämpa en auktoriseringsprincip innan UseEndpoints skickas till slutpunkten.

Slutpunktsmetadata

I föregående exempel finns det två slutpunkter, men endast slutpunkten för hälsokontroll har en auktoriseringsprincip kopplad. Om begäran matchar slutpunkten /healthzför hälsokontroll utförs en auktoriseringskontroll. Detta visar att slutpunkter kan ha extra data kopplade till sig. Dessa extra data kallas slutpunktsmetadata:

  • Metadata kan bearbetas av routningsmedvetna mellanprogram.
  • Metadata kan vara av valfri .NET-typ.

Routningsbegrepp

Routningssystemet bygger på pipelinen för mellanprogram genom att lägga till det kraftfulla slutpunktskonceptet . Slutpunkter representerar enheter i appens funktioner som skiljer sig från varandra när det gäller routning, auktorisering och valfritt antal ASP.NET Core-system.

ASP.NET Core-slutpunktsdefinition

En ASP.NET Core-slutpunkt är:

Följande kod visar hur du hämtar och inspekterar slutpunkten som matchar den aktuella begäran:

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

Slutpunkten, om den är markerad, kan hämtas från HttpContext. Dess egenskaper kan inspekteras. Slutpunktsobjekt är oföränderliga och kan inte ändras när de har skapats. Den vanligaste typen av slutpunkt är en RouteEndpoint. RouteEndpoint innehåller information som gör att den kan väljas av routningssystemet.

I föregående kod, app. Använd konfigurerar ett infogat mellanprogram.

Följande kod visar att det, beroende på var app.Use som anropas i pipelinen, kanske inte finns någon slutpunkt:

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

Det här föregående exemplet lägger till Console.WriteLine instruktioner som visar om en slutpunkt har valts eller inte. För tydlighetens skull tilldelar exemplet ett visningsnamn till den angivna / slutpunkten.

Kör den här koden med en URL på / visar:

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

Om du kör den här koden med andra URL:ar visas:

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

Dessa utdata visar att:

  • Slutpunkten är alltid null innan UseRouting anropas.
  • Om en matchning hittas är slutpunkten inte null mellan UseRouting och UseEndpoints.
  • Mellanprogrammet UseEndpoints är terminal när en matchning hittas. Terminalmellanprogram definieras senare i det här dokumentet.
  • Mellanprogrammet efter UseEndpoints körs endast när ingen matchning hittas.

Mellanprogrammet UseRouting använder metoden SetEndpoint för att koppla slutpunkten till den aktuella kontexten. Det går att ersätta UseRouting mellanprogrammet med anpassad logik och ändå få fördelarna med att använda slutpunkter. Ändpunkter är ett grundläggande element på låg nivå som mellanvaror och är inte kopplade till routningens implementering. De flesta appar behöver inte ersättas UseRouting med anpassad logik.

Mellanprogrammet UseEndpoints är utformat för att användas tillsammans med UseRouting mellanprogrammet. Kärnlogiken för att utföra en slutpunkt är inte komplicerad. Använd GetEndpoint för att hämta slutpunkten och anropa sedan dess RequestDelegate egenskap.

Följande kod visar hur mellanprogram kan påverka eller reagera på routning:

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

Föregående exempel visar två viktiga begrepp:

  • Mellanprogram kan köras tidigare UseRouting för att ändra de data som routningen körs på.
  • Mellanprogram kan köras mellan UseRouting och UseEndpoints för att bearbeta resultatet av routingen innan slutpunkten körs.
    • Mellanprogram som körs mellan UseRouting och UseEndpoints:
      • Inspekterar vanligtvis metadata för att förstå slutpunkterna.
      • Fattar ofta säkerhetsbeslut, som görs av UseAuthorization och UseCors.
    • Kombinationen av mellanprogram och metadata gör det möjligt att konfigurera principer per slutpunkt.

Föregående kod visar ett exempel på ett anpassat mellanprogram som stöder principer per slutpunkt. Mellanprogrammet skriver en revisionslogg av åtkomst av känsliga data till konsolen. Mellanprogrammet kan konfigureras för att granska en slutpunkt med AuditPolicyAttribute metadata. Det här exemplet visar ett opt-in-mönster där endast slutpunkter som är markerade som känsliga granskas. Det är möjligt att definiera den här logiken omvänt och granska allt som inte är markerat som säkert, till exempel. Slutpunktsmetadatasystemet är flexibelt. Den här logiken kan utformas på vilket sätt som helst som passar användningsfallet.

Föregående exempelkod är avsedd att demonstrera de grundläggande begreppen för slutpunkter. Exemplet är inte avsett för produktionsanvändning. En mer fullständig version av ett mellanprogram för granskningsloggar skulle:

  • Logga in på en fil eller databas.
  • Inkludera information som användaren, IP-adressen, namnet på den känsliga slutpunkten med mera.

Metadata för revisionspolicy AuditPolicyAttribute definieras som en Attribute för att enklare kunna använda klassbaserade ramverk som kontroller och SignalR. När du använder route to code:

  • Metadata bifogas med ett builder-API.
  • Klassbaserade ramverk innehåller alla attribut på motsvarande metod och klass när du skapar slutpunkter.

Metodtipsen för metadatatyper är att definiera dem antingen som gränssnitt eller attribut. Gränssnitt och attribut tillåter återanvändning av kod. Metadatasystemet är flexibelt och medför inga begränsningar.

Jämföra ett terminalmellanprogram och routning

Följande kodexempel jämför användningen av mellanprogram med användningen av routning:

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

Formatet för mellanprogram som visas med Approach 1: är terminalmellanprogram. Det kallas terminalmellanprogram eftersom det utför en matchande åtgärd:

  • Matchningsåtgärden i föregående exempel är Path == "/" för mellanprogrammet och Path == "/Movie" för routning.
  • När en matchning lyckas, utför den vissa funktioner och returnerar istället för att anropa next-mellanprogrammet.

Det kallas terminalmellanprogram eftersom det avslutar sökningen, kör vissa funktioner och sedan returnerar.

Jämföra ett terminalmellanprogram och routning:

  • Båda metoderna gör det möjligt att avsluta bearbetningspipelinen:
    • Mellanprogram avslutar pipelinen genom att returnera i stället för att nextanropa .
    • Slutpunkter är alltid terminaler.
  • Med terminalmellanprogram kan du placera mellanprogrammet på en godtycklig plats i pipelinen:
  • Med terminalmellanprogram kan godtycklig kod avgöra när mellanprogrammet matchar:
    • Anpassad vägmatchningskod kan vara utförlig och svår att skriva korrekt.
    • Routning ger enkla lösningar för typiska appar. De flesta appar kräver inte anpassad vägmatchningskod.
  • Slutpunktsgränssnitt med mellanprogram som UseAuthorization och UseCors.
    • Användning av ett terminalmellanprogram med UseAuthorization eller UseCors kräver manuell koppling med auktoriseringssystemet.

En slutpunkt definierar båda:

  • Ett ombud för att bearbeta begäranden.
  • En samling godtyckliga metadata. Metadata används för att implementera övergripande problem baserat på principer och konfiguration som är kopplade till varje slutpunkt.

Terminalmellanprogram kan vara ett effektivt verktyg, men kan kräva:

  • En betydande mängd kodning och testning.
  • Manuell integrering med andra system för att uppnå önskad flexibilitetsnivå.

Överväg att integrera med routning innan du skriver ett terminalmellanprogram.

Befintliga terminalmellanprogram som integreras med Map eller MapWhen kan vanligtvis omvandlas till en routingmedveten slutpunkt. MapHealthChecks visar mönstret för router-ware:

  • Skriv en tilläggsmetod på IEndpointRouteBuilder.
  • Skapa en pipeline för kapslade mellanprogram med CreateApplicationBuilder.
  • Koppla mellanprogrammet till den nya pipelinen. I det här fallet UseHealthChecks.
  • Build mellanprogramspipelinen till en RequestDelegate.
  • Anropa Map och ange den nya pipelinen för mellanprogram.
  • Returnera builder-objektet som tillhandahålls av Map från tilläggsmetoden.

Följande kod visar användning av 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!");
        });
    });
}

Föregående exempel visar varför det är viktigt att returnera builder-objektet. När byggobjektet returneras kan apputvecklaren konfigurera principer som auktorisering för slutpunkten. I det här exemplet har mellanprogrammet för hälsokontroller ingen direkt integrering med auktoriseringssystemet.

Metadatasystemet skapades som svar på de problem som uppstått av utökningsförfattare med hjälp av terminalmellanprogram. Det är problematiskt för varje mellanprogram att implementera sin egen integrering med auktoriseringssystemet.

URL-matchning

  • Är den process genom vilken routningen matchar en inkommande begäran till en slutpunkt.
  • Baseras på data i URL-sökvägen och rubrikerna.
  • Kan utökas för att överväga alla data i begäran.

När ett routningsmellanprogram körs anger det en Endpoint och dirigerar värden till en begäransfunktionHttpContext från den aktuella begäran:

  • Genom att anropa HttpContext.GetEndpoint hämtas slutpunkten.
  • HttpRequest.RouteValues hämtar samlingen av routvärden.

Mellanprogram körs efter att routningsmellanprogram har möjlighet att inspektera slutpunkten och vidta åtgärder. Till exempel kan ett mellanprogramvara för auktorisering granska och utvärdera slutpunktens metadatasamling för en auktoriseringspolicy. När alla mellanprogram i pipelinen för bearbetning av begäranden har körts anropas den valda slutpunktens ombud.

Routningssystemet i slutpunktsroutningen ansvarar för alla leveransbeslut. Eftersom mellanprogrammet tillämpar principer baserat på den valda slutpunkten är det viktigt att:

  • Alla beslut som kan påverka sändningen eller tillämpningen av säkerhetsprinciper fattas i routningssystemet.

Warning

För bakåtkompatibilitet, när en kontroller eller Razor Pages-slutpunktsdelegat körs, anges egenskaperna för RouteContext.RouteData till lämpliga värden baserat på den bearbetning av begäranden som utförts hittills.

Typen RouteContext kommer att markeras som föråldrad i en framtida version:

  • Migrera RouteData.Values till HttpRequest.RouteValues.
  • Migrera RouteData.DataTokens för att hämta IDataTokensMetadata från slutpunktsmetadata.

URL-matchning fungerar i en konfigurerbar uppsättning faser. I varje fas är utdata en uppsättning matchningar. Mängden matcher kan begränsas ytterligare under nästa fas. Routningsimplementeringen garanterar inte någon bearbetningsordning för matchande slutpunkter. Alla möjliga matchningar bearbetas samtidigt. Url-matchningsfaserna sker i följande ordning. ASP.NET Core:

  1. Bearbetar URL-sökvägen mot uppsättningen slutpunkter och deras vägmallar och samlar in alla matchningar.
  2. Tar den föregående listan och tar bort matchningar som inte uppfyller ruttbegränsningar som tillämpas.
  3. Tar den föregående listan och tar bort matchningar som inte uppfyller kraven i MatcherPolicy-instanser.
  4. Använder EndpointSelector för att fatta ett slutgiltigt beslut från föregående lista.

Listan över slutpunkter prioriteras enligt:

Alla matchande ändpunkter bearbetas i varje fas tills den EndpointSelector har nåtts. EndpointSelector är den sista fasen. Den väljer den högsta prioritetsslutpunkten från matchningarna som den bästa matchningen. Om det finns andra matchningar med samma prioritet som den bästa matchningen, kommer ett tvetydigt matchningsundantag att utlösas.

Routningsprioriteten beräknas baserat på att en mer specifik vägmall får högre prioritet. Tänk till exempel på mallarna /hello och /{message}:

  • Båda matchar URL-sökvägen /hello.
  • /hello är mer specifik och därför högre prioritet.

I allmänhet gör routningsprioriteten ett bra jobb med att välja den bästa matchningen för de typer av URL-scheman som används i praktiken. Använd Order endast när det behövs för att undvika tvetydigheter.

På grund av de typer av utökningsbarhet som tillhandahålls av routning är det inte möjligt för routningssystemet att beräkna de tvetydiga vägarna i förväg. Överväg ett exempel som routningsmallarna /{message:alpha} och /{message:int}:

  • Villkoret alpha matchar endast alfabetiska tecken.
  • Villkoret int matchar endast tal.
  • Dessa mallar har samma vägprioritet, men det finns ingen enskild URL som båda matchar.
  • Om routningssystemet rapporterade ett tvetydighetsfel vid starten skulle det blockera det här giltiga användningsfallet.

Warning

Ordningen på åtgärderna i UseEndpoints påverkar inte beteendet för routning, med ett undantag. MapControllerRoute och MapAreaRoute tilldela automatiskt ett ordervärde till sina slutpunkter baserat på den ordning som de anropas. Detta simulerar långvarigt beteende för kontrollanter utan att routningssystemet ger samma garantier som äldre routningsimplementeringar.

I den äldre implementeringen av routning är det möjligt att implementera utökningsbarhet för routning som har ett beroende av i vilken ordning vägar bearbetas. Slutpunktsroutning i ASP.NET Core 3.0 eller senare:

  • Har inget begrepp om vägar.
  • Ger inte ordergarantier. Alla slutpunkter bearbetas samtidigt.

Routningsmallens prioritet och slutpunktsmarkeringsordning

Prioritet för routningsmallar är ett system som tilldelar varje vägmall ett värde baserat på hur specifik den är. Prioritet för routningsmall:

  • Undviker behovet av att justera ordningen på slutpunkter i vanliga fall.
  • Försöker matcha allmänna förväntningar på routingbeteende.

Du kan till exempel överväga mallar /Products/List och /Products/{id}. Det är rimligt att anta att det /Products/List är en bättre matchning än /Products/{id} för URL-sökvägen /Products/List. Detta fungerar eftersom literalsegmentet /List anses ha bättre prioritet än parametersegmentet /{id}.

Information om hur prioritet fungerar är kopplat till hur routningsmallar definieras:

  • Mallar med fler segment anses vara mer specifika.
  • Ett segment med literaltext anses vara mer specifikt än ett parametersegment.
  • Ett parametersegment med en begränsning anses vara mer specifikt än ett utan.
  • Ett komplext segment anses vara lika specifikt som ett parametersegment med en begränsning.
  • Catch-all-parametrar är de minst specifika. Se catch-all i referensen för routningsmallen för viktig information om catch-all-vägar.

Se källkoden på GitHub för en referens till exakta värden.

Begrepp för URL-generering

URL-generering:

  • Är den process genom vilken routning kan skapa en URL-sökväg baserat på en uppsättning vägvärden.
  • Tillåter en logisk separation mellan slutpunkter och URL:er som har åtkomst till dem.

Slutpunktsroutning innehåller API:et LinkGenerator . LinkGenerator är en singleton-tjänst som är tillgänglig från DI. API LinkGenerator kan användas utanför kontexten för en exekverande begäran. Mvc.IUrlHelper och scenarier som förlitar sig på IUrlHelper, till exempel Tag Helpers, HTML Helpers och Action Results, använder API:et LinkGenerator internt för att tillhandahålla funktioner för länkgenerering.

Länkgeneratorn backas upp av begreppet adress - och adressscheman. Ett adressschema är ett sätt att fastställa de slutpunkter som ska beaktas för länkgenerering. Till exempel implementeras scenarier med routningnamn och routvärden som många användare är bekanta med från kontroller och Razor sidor såsom ett adressschema.

Länkgeneratorn kan länka till styrenheter och Razor sidor via följande tilläggsmetoder:

Överbelastningar av dessa metoder tar emot argument som inkluderar HttpContext. Dessa metoder är funktionellt likvärdiga med Url.Action och Url.Page, men ger ytterligare flexibilitet och alternativ.

Metoderna GetPath* liknar Url.Action mest och Url.Page, eftersom de genererar en URI som innehåller en absolut sökväg. Metoderna GetUri* genererar alltid en absolut URI som innehåller ett schema och en värd. De metoder som accepterar en HttpContext genererar en URI i kontexten för den körande begäran. Värden ambienta för väg, URL-bassökväg, protokoll och värd från den körande begäran används om de inte åsidosätts.

LinkGenerator anropas med en adress. Generera en URI sker i två steg:

  1. En adress är bunden till en lista över slutpunkter som matchar adressen.
  2. Varje slutpunkts RoutePattern utvärderas tills ett vägmönster som matchar de angivna värdena hittas. Resultatet kombineras med de andra URI-delarna som levereras till länkgeneratorn och returneras.

Metoderna som tillhandahålls av LinkGenerator stöder standardfunktioner för länkgenerering för alla typer av adresser. Det enklaste sättet att använda länkgeneratorn är genom tilläggsmetoder som utför åtgärder för en viss adresstyp:

Tilläggsmetod Description
GetPathByAddress Genererar en URI med en absolut sökväg baserat på de angivna värdena.
GetUriByAddress Genererar en absolut URI baserat på de angivna värdena.

Warning

Var uppmärksam på följande konsekvenser av att anropa LinkGenerator metoder:

  • Använd GetUri* tilläggsmetoder med försiktighet i en appkonfiguration som inte validerar headern för inkommande begäranden. Host Om rubriken för inkommande begäranden inte verifieras kan ej betrodda begärandeindata skickas tillbaka till klienten i URI:er i en vy eller sida. Vi rekommenderar att alla produktionsappar konfigurerar sin server för att kontrollera Host headern mot kända giltiga värden.

  • Använd LinkGenerator med försiktighet i mellanprogram i kombination med Map eller MapWhen. Map* ändrar basvägen för den körande begäran, vilket påverkar utdata från länkgenereringen. LinkGenerator Alla API:er tillåter att du anger en bassökväg. Ange en tom bassökväg för att ta bort Map* effekten på länkgenereringen.

Exempel på mellanprogram

I följande exempel använder ett mellanprogram API:et LinkGenerator för att skapa en länk till en åtgärdsmetod som visar lagerprodukter. Använda länkgeneratorn genom att mata in den i en klass och anrop GenerateLink är tillgängligt för alla klasser i en app:

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

Referens för routningsmall

Token inom {} definierar ruttparametrar som är bundna om rutten matchas. Mer än en vägparameter kan definieras i ett vägsegment, men vägparametrarna måste avgränsas med ett literalvärde. Till exempel {controller=Home}{action=Index} är inte en giltig väg, eftersom det inte finns något literalvärde mellan {controller} och {action}. Routningsparametrarna måste ha ett namn och kan ha ytterligare attribut angivna.

Annan literaltext än vägparametrar (till exempel {id}) och sökvägsavgränsaren / måste matcha texten i URL:en. Textmatchning är skiftlägesokänslig och baseras på den avkodade representationen av URL:ens sökväg. Om du vill matcha en literalvägsparameter avgränsare { eller }kan du undvika avgränsare genom att upprepa tecknet. Till exempel {{ eller }}.

Asterisk * eller dubbel asterisk **:

  • Kan användas som ett prefix till en vägparameter för att binda till resten av URI:n.
  • Kallas för catch-all-parametrar. Till exempel blog/{**slug}:
    • Matchar alla URI:er som börjar med /blog och har ett värde som följer den.
    • Värdet som följer efter /blog tilldelas som värde för slug-routen.

Warning

En parameter kan matcha vägar felaktigt på grund av en bugg i routning. Appar som påverkas av den här buggen har följande egenskaper:

  • En allomfattande rutt, till exempel {**slug}"
  • Catch-all-vägen matchar inte begäranden som den ska matcha.
  • Om du tar bort andra rutter börjar den allomfattande rutten att fungera.

Se GitHub-buggar 18677 och 16579 till exempel fall som drabbat den här buggen.

En anmälningskorrigering för den här buggen finns i .NET Core 3.1.301 eller senare SDK. Följande kod anger en intern växel som åtgärdar felet:

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

Catch-all-parametrar kan även matcha den tomma strängen.

Parametern catch-all undflyr lämpliga tecken när vägen används för att generera en URL, inklusive sökvägsavgränsningstecken / . Till exempel genererar rutten foo/{*path} med ruttvärden { path = "my/path" }foo/my%2Fpath. Observera det undantagna snedstrecket. Använd routningsparameterprefixet ** för att avgränsa sökvägens avgränsningstecken. Vägen foo/{**path} med { path = "my/path" } genererar foo/my/path.

URL-mönster som försöker avbilda ett filnamn med ett valfritt filnamnstillägg har ytterligare överväganden. Överväg till exempel mallen files/{filename}.{ext?}. När värden för båda filename och ext finns fylls båda värdena i. Om det bara finns ett värde för filename i URL:en matchar vägen eftersom avslutandet . är valfritt. Följande URL:er matchar den här vägen:

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

Routningsparametrar kan ha standardvärden som anges genom att ange standardvärdet efter parameternamnet avgränsat med ett likhetstecken (=). Definierar {controller=Home}Home till exempel som standardvärde för controller. Standardvärdet används om inget värde finns i URL:en för parametern. Routningsparametrar görs valfria genom att ett frågetecken (?) läggs till i slutet av parameternamnet. Till exempel id?. Skillnaden mellan valfria värden och standardvägsparametrar är:

  • En routningsparameter med ett standardvärde genererar alltid ett värde.
  • En valfri parameter har endast ett värde när ett värde tillhandahålls av begärande-URL:en.

Ruttparametrarna kan ha begränsningar som måste matcha ruttvärdet som är bundet från URL:en. Att lägga till : och villkorsnamn efter namnet på routningsparametern anger en infogad begränsning för en routningsparameter. Om villkoret kräver argument omges de av parenteser (...) efter villkorsnamnet. Du kan ange flera inline-begränsningar genom att lägga till en annan : och ett annat begränsningsnamn.

Villkorsnamnet och argumenten IInlineConstraintResolver skickas till tjänsten för att skapa en instans av IRouteConstraint som ska användas i URL-bearbetning. Routningsmallen blog/{article:minlength(10)} anger till exempel en minlength begränsning med argumentet 10. Mer information om routningsbegränsningar och en lista över de begränsningar som tillhandahålls av ramverket finns i avsnittet Referens för routningsbegränsningar .

Routningsparametrar kan också ha parametertransformatorer. Parametertransformatorer transformerar en parameters värde vid generering av länkar och matchande åtgärder och sidor till URL:er. Precis som begränsningar kan parametertransformatorer läggas till direkt i en routparameter genom att lägga till ett : och ett transformeringsnamn efter routparameternamnet. Routningsmallen blog/{article:slugify} anger till exempel en slugify transformerare. Mer information om parametertransformatorer finns i referensavsnittet Parametertransformator .

I följande tabell visas exempel på routningsmallar och deras beteende:

Routningsmall Exempel på matchande URI Förfrågan-URI:n...
hello /hello Matchar endast den ensamma sökvägen /hello.
{Page=Home} / Matchar och anger Page till Home.
{Page=Home} /Contact Matchar och anger Page till Contact.
{controller}/{action}/{id?} /Products/List Mappar till kontroller Products och åtgärd List.
{controller}/{action}/{id?} /Products/Details/123 Mappar till kontrollanten Products och Details åtgärden medid värdet 123.
{controller=Home}/{action=Index}/{id?} / Mappar till Home-kontrollern och Index-metoden. id ignoreras.
{controller=Home}/{action=Index}/{id?} /Products Mappar till Products-kontrollern och Index-metoden. id ignoreras.

Att använda en mall är vanligtvis den enklaste metoden för routning. Begränsningar och standardvärden kan också anges utanför vägmallen.

Komplexa segment

Komplexa segment bearbetas genom matchning av literalavgränsare från höger till vänster på ett icke-girigt sätt. Till exempel [Route("/a{b}c{d}")] är ett komplext segment. Komplexa segment fungerar på ett visst sätt som måste tolkas för att kunna använda dem. Exemplet i det här avsnittet visar varför komplexa segment bara fungerar bra när avgränsartexten inte visas i parametervärdena. Att använda en regex och sedan extrahera värdena manuellt behövs för mer komplexa fall.

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Det här är en sammanfattning av de steg som routningen utför med mallen /a{b}c{d} och URL-sökvägen /abcd. | Används för att visualisera hur algoritmen fungerar:

  • Den första literalen, från höger till vänster, är c. Så /abcd söks från höger och hittar /ab|c|d.
  • Allt till höger (d) matchas nu med routningsparametern {d}.
  • Nästa literal, från höger till vänster, är a. Så /ab|c|d genomsöks från där vi slutade, sedan hittas a/|a|b|c|d.
  • Värdet till höger (b) matchas nu med routningsparametern {b}.
  • Det finns ingen kvarvarande text och ingen kvarvarande mall för rutt, så det här är en matchning.

Här är ett exempel på ett negativt ärende med samma mall /a{b}c{d} och URL-sökvägen /aabcd. | Används för att visualisera hur algoritmen fungerar. Det här fallet är inte en matchning, vilket förklaras av samma algoritm:

  • Den första literalen, från höger till vänster, är c. Så /aabcd söks från höger och hittar /aab|c|d.
  • Allt till höger (d) matchas nu med routningsparametern {d}.
  • Nästa literal, från höger till vänster, är a. Så /aab|c|d genomsöks från där vi slutade, sedan hittas a/a|a|b|c|d.
  • Värdet till höger (b) matchas nu med routningsparametern {b}.
  • I det här läget finns det fortfarande text a, men algoritmen har slut på vägmall för analys, så detta stämmer inte.

Eftersom matchningsalgoritmen inte är girig:

  • Den matchar den minsta möjliga mängden text i varje steg.
  • Alla fall där avgränsarvärdet visas i parametervärdena resulterar i att det inte matchar.

Reguljära uttryck ger mycket mer kontroll över deras matchande beteende.

Girig matchning, också kallad försiktig matchning, matchar den största möjliga strängen. Icke-giriga matchar den minsta möjliga strängen.

Referens för routningsbegränsningar

Routningsbegränsningar körs när en matchning har hittats för den inkommande URL:en och URL-sökvägen tokeniseras till ruttvärden. Routningsbegränsningar inspekterar vanligtvis det vägvärde som är associerat via routningsmallen och fattar ett sant eller falskt beslut om huruvida värdet är acceptabelt. Vissa routningsbegränsningar använder data utanför vägvärdet för att överväga om begäran kan dirigeras. Till exempel HttpMethodRouteConstraint kan acceptera eller avvisa en begäran baserat på dess HTTP-verb. Begränsningar används i routningsbegäranden och länkgenerering.

Warning

Använd inte begränsningar för indataverifiering. Om begränsningar används för indataverifiering resulterar ogiltiga indata i ett 404 svar som inte hittades. Ogiltiga indata bör generera en 400 felaktig begäran med ett lämpligt felmeddelande. Routningsbegränsningar används för att särskilja liknande rutter, inte för att verifiera inmatningar för en viss rutt.

I följande tabell visas exempel på vägbegränsningar och deras förväntade beteende:

constraint Example Exempelmatchningar Notes
int {id:int} 123456789, -123456789 Matchar alla heltal
bool {active:bool} true, FALSE Matchar true eller false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Matchar ett giltigt DateTime värde i den invarianta kulturen. Se föregående varning.
decimal {price:decimal} 49.99, -1,000.01 Matchar ett giltigt decimal värde i den invarianta kulturen. Se föregående varning.
double {weight:double} 1.234, -1,001.01e8 Matchar ett giltigt double värde i den invarianta kulturen. Se föregående varning.
float {weight:float} 1.234, -1,001.01e8 Matchar ett giltigt float värde i den invarianta kulturen. Se föregående varning.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Matchar ett giltigt Guid värde
long {ticks:long} 123456789, -123456789 Matchar ett giltigt long värde
minlength(value) {username:minlength(4)} Rick Strängen måste innehålla minst 4 tecken
maxlength(value) {filename:maxlength(8)} MyFile Strängen får inte innehålla fler än 8 tecken
length(length) {filename:length(12)} somefile.txt Strängen måste vara exakt 12 tecken lång
length(min,max) {filename:length(8,16)} somefile.txt Strängen får vara minst 8 och högst 16 tecken lång
min(value) {age:min(18)} 19 Heltalsvärdet måste vara minst 18
max(value) {age:max(120)} 91 Heltalsvärdet får inte vara mer än 120
range(min,max) {age:range(18,120)} 91 Heltalsvärdet måste vara minst 18 men högst 120
alpha {name:alpha} Rick Strängen måste bestå av ett eller flera alfabetiska tecken, och vara skiftlägesokänslig, a-z.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 Strängen måste matcha det reguljära uttrycket. Se tips om hur du definierar ett reguljärt uttryck.
required {name:required} Rick Används för att framtvinga att ett icke-parametervärde finns under URL-generering

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Flera, kolonavgränsade begränsningar kan tillämpas på en enskild parameter. Följande villkor begränsar till exempel en parameter till ett heltalsvärde på 1 eller högre:

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

Warning

Routningsbegränsningar som verifierar URL:en och konverteras till en CLR-typ använder alltid den invarianta kulturen. Till exempel konvertering till CLR-typen int eller DateTime. Dessa begränsningar förutsätter att URL:en inte kan lokaliseras. De ramverksbaserade vägbegränsningarna ändrar inte de värden som lagras i vägvärden. Alla vägvärden som parsas från URL:en lagras som strängar. Villkoret float försöker till exempel konvertera vägvärdet till en flyttal, men det konverterade värdet används bara för att verifiera att det kan konverteras till en flyttal.

Reguljära uttryck i begränsningar

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Reguljära uttryck kan anges som infogade begränsningar med hjälp av routningsbegränsningen regex(...) . Metoder i MapControllerRoute familjen accepterar också en objektliteral av begränsningar. Om formuläret används tolkas strängvärden som reguljära uttryck.

Följande kod använder en infogad regex-begränsning:

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
        context => 
        {
            return context.Response.WriteAsync("inline-constraint match");
        });
 });

Följande kod använder en objektliteral för att ange en regex-begränsning:

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

Ramverket ASP.NET Core lägger RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant till konstruktorn för reguljära uttryck. Se RegexOptions en beskrivning av dessa medlemmar.

Reguljära uttryck använder avgränsare och token som liknar dem som används vid routning och C#-språket. Token för reguljära uttryck måste vara undantagna. Om du vill använda reguljära uttryck ^\d{3}-\d{2}-\d{4}$ i en infogad begränsning använder du något av följande:

  • Ersätt \ tecken som anges i strängen som \\ tecken i C#-källfilen för att undvika strängens \ escape-tecken.
  • Verbatim strängliteraler.

Om du vill undvika dirigeringsparametertecken {, }, [, ], dubblar du tecknen i uttrycket, {{till exempel , }}, [[, . ]] I följande tabell visas ett reguljärt uttryck och dess undantagna version:

Reguljärt uttryck Undantagna reguljära uttryck
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Reguljära uttryck som används i routning börjar ofta med ^ tecknet och matchar strängens startposition. Uttrycken slutar ofta med $ tecknet och matchar slutet av strängen. Tecknen ^ och $ ser till att det reguljära uttrycket matchar värdet för hela routningsparametern. Utan ^- och $-tecknen matchar det reguljära uttrycket vilken delsträng som helst i strängen, vilket ofta är oönskat. Följande tabell innehåller exempel och förklarar varför de matchar eller inte matchar:

Expression String Match Comment
[a-z]{2} hello Yes Matchningar för delsträngar
[a-z]{2} 123abc456 Yes Matchningar för delsträngar
[a-z]{2} mz Yes Matchar uttryck
[a-z]{2} MZ Yes Inte skiftlägeskänslig
^[a-z]{2}$ hello No Se ^ och $ ovan
^[a-z]{2}$ 123abc456 No Se ^ och $ ovan

Mer information om syntax för reguljära uttryck finns i Reguljära .NET Framework-uttryck.

Om du vill begränsa en parameter till en känd uppsättning möjliga värden använder du ett reguljärt uttryck. Till exempel {action:regex(^(list|get|create)$)} matchar endast routningsvärdet action till list, geteller create. Om den skickas in i begränsningsordboken, så är strängen ^(list|get|create)$ likvärdig. Begränsningar som skickas i villkorsordlistan som inte matchar någon av de kända begränsningarna behandlas också som reguljära uttryck. Begränsningar som skickas i en mall som inte matchar någon av de kända begränsningarna behandlas inte som reguljära uttryck.

Begränsningar för anpassad rutt

Anpassade routningsbegränsningar kan skapas genom att implementera IRouteConstraint gränssnittet. Gränssnittet IRouteConstraint innehåller Match, som returnerar true om villkoret är uppfyllt och false på annat sätt.

Anpassade routningsbegränsningar behövs sällan. Innan du implementerar en anpassad vägbegränsning bör du överväga alternativ, till exempel modellbindning.

Mappen ASP.NET Core Constraints innehåller bra exempel på hur du skapar begränsningar. Till exempel GuidRouteConstraint.

Om du vill använda en anpassad IRouteConstraintmåste routningsbegränsningstypen registreras med appens ConstraintMap i tjänstcontainern. A ConstraintMap är en ordlista som mappar routningsbegränsningsnycklar till IRouteConstraint implementeringar som validerar dessa begränsningar. En apps ConstraintMap kan uppdateras i Startup.ConfigureServices antingen som en del av en services.AddRouting-anrop eller genom att konfigurera RouteOptions direkt med services.Configure<RouteOptions>. Till exempel:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddRouting(options =>
    {
        options.ConstraintMap.Add("customName", typeof(MyCustomConstraint));
    });
}

Föregående villkor tillämpas i följande kod:

[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 tillhandahålls av Rick.Docs.Samples.RouteInfo NuGet-paketet och visar väginformation.

Implementeringen av MyCustomConstraint förhindrar 0 att en routningsparameter tillämpas:

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

Warning

När du använder System.Text.RegularExpressions för att bearbeta ej betrodda indata skickar du en timeout. En obehörig användare kan ange indata för att RegularExpressions orsaka en Denial-of-Service-attack. ASP.NET Core Frameworks API som använder RegularExpressions har en timeout.

Föregående kod:

  • Förhindrar 0 i {id} segmentet av rutten.
  • Visas som ett grundläggande exempel på hur du implementerar en anpassad begränsning. Den bör inte användas i en produktionsapp.

Följande kod är en bättre metod för att förhindra att en id innehållande en 0 bearbetas:

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

    return ControllerContext.MyDisplayRouteInfo(id);
}

Föregående kod har följande fördelar jämfört med MyCustomConstraint metoden:

  • Det kräver ingen anpassad begränsning.
  • Det returnerar ett mer beskrivande fel när vägparametern innehåller 0.

Referens för parametertransformator

Parametertransformatorer:

Till exempel, en anpassad slugify-parametertransformator i vägmönster blog\{article:slugify} med Url.Action(new { article = "MyTestArticle" }) genererar blog\my-test-article.

Överväg följande IOutboundParameterTransformer implementering:

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

Om du vill använda en parametertransformator i ett vägmönster konfigurerar du den med hjälp av ConstraintMap i Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddRouting(options =>
    {
        options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
    });
}

ASP.NET Core-ramverket använder parametertransformatorer för att transformera den URI där en slutpunkt löser problemet. Till exempel transformerar parametertransformatorer de vägvärden som används för att matcha en area, controller, actionoch page.

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

Med föregående vägmall matchas åtgärden SubscriptionManagementController.GetAll med URI /subscription-management/get-all:n . En parametertransformator ändrar inte de vägvärden som används för att generera en länk. Till exempel Url.Action("GetAll", "SubscriptionManagement") ger /subscription-management/get-all.

ASP.NET Core tillhandahåller API-konventioner för användning av parametertransformatorer med genererade vägar:

Referens för URL-generering

Det här avsnittet innehåller en referens för algoritmen som implementeras av URL-generering. I praktiken använder de flesta komplexa exempel på URL-generering styrenheter eller Razor sidor. Mer information finns i routning i kontrollanter .

URL-genereringsprocessen börjar med ett anrop till LinkGenerator.GetPathByAddress eller en liknande metod. Metoden tillhandahålls med en adress, en uppsättning vägvärden och eventuellt information om den aktuella begäran från HttpContext.

Det första steget är att använda adressen för att lösa en uppsättning kandidatslutpunkter med hjälp av en IEndpointAddressScheme<TAddress> som matchar adressens typ.

När uppsättningen kandidater hittas av adressschemat sorteras slutpunkterna och bearbetas iterativt tills en URL-genereringsåtgärd lyckas. URL-genereringen söker inte efter tvetydigheter. Det första resultatet som returneras är slutresultatet.

Felsöka URL-generering med loggning

Det första steget i felsökning av URL-generering för Microsoft.AspNetCore.Routing är att ställa in loggningsnivån till TRACE. LinkGenerator loggar många detaljer om bearbetningen som kan vara användbar för att felsöka problem.

Mer information om URL-generering finns i REFERENS för URL-generering .

Addresses

Adresser är det begrepp i URL-generering som används för att binda ett anrop till länkgeneratorn till en uppsättning kandidatslutpunkter.

Adresser är ett utökningsbart begrepp som levereras med två implementeringar som standard:

  • Använda slutpunktsnamnet (string) som adress:
    • Tillhandahåller liknande funktioner som MVC:s routningsnamn.
    • IEndpointNameMetadata Använder metadatatypen.
    • Löser den angivna strängen mot metadata för alla registrerade slutpunkter.
    • Utlöser ett undantag vid start om flera slutpunkter använder samma namn.
    • Rekommenderas för generell användning utanför kontrollanter och Razor sidor.
  • Använda vägvärden (RouteValuesAddress) som adress:
    • Ger liknande funktioner som kontroller och äldre URL-generering för sidor.
    • Mycket komplext att utöka och felsöka.
    • Tillhandahåller implementeringen som används av IUrlHelper, Tag Helpers, HTML-hjälpare, åtgärdsresultat osv.

Adressschemats roll är att göra associationen mellan adressen och matchande slutpunkter enligt godtyckliga kriterier:

  • Slutpunktsnamnschemat utför en grundläggande ordlistesökning.
  • Routningsvärdena har en komplex bästa delmängd av set-algoritmen.

Omgivande värden och explicita värden

Från den aktuella begäran får routning åtkomst till routningsvärdena för den aktuella begäran HttpContext.Request.RouteValues. De värden som är associerade med den aktuella begäran kallas för omgivande värden. För tydlighetens skull refererar dokumentationen till de vägvärden som skickas in till metoder som explicita värden.

I följande exempel visas omgivande värden och explicita värden. Den innehåller omgivande värden från den aktuella begäran och explicita värden: : { 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);
    }

Föregående kod:

Följande kod innehåller inga omgivande värden och explicita värden: : { controller = "Home", action = "Subscribe", id = 17, }

public IActionResult Index2()
{
    var url = _linkGenerator.GetPathByAction("Subscribe", "Home",
                                             new { id = 17, });
    return Content(url);
}

Föregående metod returnerar /Home/Subscribe/17

Följande kod i WidgetController returnerar /Widget/Subscribe/17:

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

Följande kod tillhandahåller kontrollen från omgivande värden i den pågående begäran och explicita värden: { action = "Edit", id = 17, }

public class GadgetController : Controller
{
    public IActionResult Index()
    {
        var url = Url.Action("Edit", new { id = 17, });
        return Content(url);
    }

I koden ovan:

  • /Gadget/Edit/17 returneras.
  • Url hämtar IUrlHelper.
  • Action genererar en URL med en absolut sökväg för en åtgärdsmetod. URL:en innehåller det angivna action namnet och route värdena.

Följande kod innehåller omgivande värden från den aktuella begäran och explicita värden: : { page = "./Edit, id = 17, }

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

Den föregående koden sätter url till /Edit/17 när Redigera Razor Sida innehåller följande siddirektiv:

@page "{id:int}"

Om sidan Redigera inte innehåller "{id:int}"-routemallen, är url/Edit?id=17.

Beteendet för MVC lägger IUrlHelper till ett lager av komplexitet utöver de regler som beskrivs här:

  • IUrlHelper tillhandahåller alltid vägvärdena från den aktuella begäran som omgivande värden.
  • IUrlHelper.Action kopierar alltid aktuella action värden och controller vägvärden som explicita värden om de inte åsidosätts av utvecklaren.
  • IUrlHelper.Page kopierar alltid det aktuella page vägvärdet som ett explicit värde om det inte åsidosätts.
  • IUrlHelper.Page åsidosätter alltid det aktuella handler ruttvärdet med null som ett explicit värde såvida det inte åsidosätts.

Användarna blir ofta förvånade över beteendeinformationen för omgivande värden, eftersom MVC inte verkar följa sina egna regler. Av historiska och kompatibilitetsskäl har vissa routningsvärden som action, controller, pageoch handler ett eget specialfallsbeteende.

Motsvarande funktionalitet som tillhandahålls av LinkGenerator.GetPathByAction och LinkGenerator.GetPathByPage duplicerar dessa avvikelser i IUrlHelper för kompatibilitet.

Process för URL-generering

När uppsättningen kandidatslutpunkter hittas, algoritmen för URL-generering:

  • Bearbetar slutpunkterna iterativt.
  • Returnerar det första lyckade resultatet.

Det första steget i den här processen kallas ogiltigförklarande av routningsvärden. Ogiltigförklaring av routningsvärden är den process genom vilken routning avgör vilka routningsvärden från bakgrundsvärden som ska användas och vilka som ska ignoreras. Varje omgivande värde beaktas och kombineras antingen med explicita värden eller ignoreras.

Det bästa sättet att förstå rollen av omgivande värden är att de försöker spara skrivande för programutvecklare i vissa vanliga situationer. Traditionellt är scenarier där omgivande värden är användbara relaterade till MVC:

  • När du länkar till en annan åtgärd i samma kontrollant behöver inte kontrollantnamnet anges.
  • När du länkar till en annan kontrollant i samma område behöver du inte ange områdesnamnet.
  • När du länkar till samma åtgärdsmetod behöver du inte ange vägvärden.
  • När du länkar till en annan del av appen vill du inte överföra routningsvärden som inte har någon betydelse i den delen av appen.

Anrop till LinkGenerator eller IUrlHelper som returnerar null orsakas vanligtvis av bristande förståelse av ogiltigförklaring av routningsvärden. Felsöka routningsvärdets ogiltighet genom att uttryckligen ange fler av vägvärdena för att se om det löser problemet.

Ogiltigförklaring av routvärden bygger på antagandet att appens URL-schema är hierarkiskt, med en hierarki som bildas från vänster till höger. Överväg den grundläggande mallen för styrenhetsroutning {controller}/{action}/{id?} för att få en intuitiv uppfattning om hur detta fungerar i praktiken. En ändring av ett värde ogiltigförklarar alla vägvärden som visas till höger. Detta återspeglar antagandet om hierarki. Om appen har ett omgivande värde för id, och åtgärden anger ett annat värde för controller:

  • id återanvänds inte eftersom {controller} är till vänster om {id?}.

Några exempel som visar den här principen:

  • Om de explicita värdena innehåller ett värde för idignoreras det omgivande värdet för id . Omgivande värden för controller och action kan användas.
  • Om de explicita värdena innehåller ett värde för actionignoreras alla omgivande värden för action . Omgivningsvärdena för controller kan användas. Om det explicita värdet för action skiljer sig från det omgivande värdet för actionid används inte värdet. Om det tydliga värdet för action är detsamma som det omgivande värdet för action, kan id värdet användas.
  • Om de explicita värdena innehåller ett värde för controllerignoreras alla omgivande värden för controller . Om det explicita värdet för controller skiljer sig från det omgivande värdet för controlleraction används inte värdena ochid. Om det explicita värdet för controller är detsamma som det omgivande värdet för controlleraction kan värdena och id användas.

Den här processen kompliceras ytterligare av förekomsten av attributvägar och dedikerade konventionella vägar. Styrenheters konventionella vägar som {controller}/{action}/{id?} specificerar en hierarki med hjälp av ruttparametrar. För dedikerade konventionella vägar och attributvägar till styrningar och Razor sidor:

  • Det finns en hierarki med vägvärden.
  • De visas inte i mallen.

I dessa fall definierar URL-generering det nödvändiga värdekonceptet . Slutpunkter som skapats av kontrollanter och Razor sidor har angivna värden som gör att vägvärdets ogiltighet kan fungera.

Ruttvärdets ogiltighetsalgoritm förklarad:

  • De nödvändiga värdenamnen kombineras med vägparametrarna och bearbetas sedan från vänster till höger.
  • För varje parameter jämförs det omgivande värdet och det explicita värdet:
    • Om det omgivande värdet och det explicita värdet är samma fortsätter processen.
    • Om det omgivande värdet finns och det explicita värdet inte är det används det omgivande värdet när URL:en genereras.
    • Om det omgivande värdet inte finns och det explicita värdet är avvisar du det omgivande värdet och alla efterföljande omgivande värden.
    • Om det omgivande värdet och det explicita värdet finns, och de två värdena är olika, avvisar du det omgivande värdet och alla efterföljande omgivande värden.

I det här läget är URL-genereringsåtgärden redo att utvärdera routningsbegränsningar. Uppsättningen godkända värden kombineras med parameterns standardvärden, vilka tillhandahålls för begränsningar. Om alla begränsningar uppfylls fortsätter operationen.

Därefter kan de godkända värdena användas för att expandera routningsmallen. Vägmallen bearbetas:

  • Från vänster till höger.
  • Varje parameter har sitt godkända värde ersatt.
  • Med följande specialfall:
    • Om de godkända värdena saknar ett värde och parametern har ett standardvärde används standardvärdet.
    • Om de godkända värdena saknar ett värde och parametern är valfri fortsätter bearbetningen.
    • Om en routningsparameter till höger om en valfri parameter som saknas har ett värde misslyckas åtgärden.
    • Sammanhängande standardvärdeparametrar och valfria parametrar komprimeras där det är möjligt.

Värden som uttryckligen anges som inte matchar ett segment av vägen läggs till i frågesträngen. Följande tabell visar resultatet när du använder routningsmallen {controller}/{action}/{id?}.

Omgivande värden Explicita värden Result
controller = "Home" action = "Om" /Home/About
controller = "Home" controller = "Beställning", action = "Om" /Order/About
controller = "Home", color = "Röd" action = "Om" /Home/About
controller = "Home" action = "Om", color = "Röd" /Home/About?color=Red

Problem med att routningsvärdet är ogiltigt

Från och med ASP.NET Core 3.0 fungerar vissa URL-generationsscheman som används i tidigare ASP.NET Core-versioner inte bra med URL-generering. ASP.NET Core-teamet planerar att lägga till funktioner för att hantera dessa behov i en framtida version. För tillfället är den bästa lösningen att använda äldre routning.

Följande kod visar ett exempel på ett URL-genereringsschema som inte stöds av routning.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute("default", 
                                     "{culture}/{controller=Home}/{action=Index}/{id?}");
    endpoints.MapControllerRoute("blog", "{culture}/{**slug}", 
                                      new { controller = "Blog", action = "ReadPost", });
});

I föregående kod culture används routningsparametern för lokalisering. Önskan är att parametern culture alltid ska accepteras som ett omgivande värde. Parametern culture accepteras dock inte som ett omgivande värde på grund av hur nödvändiga värden fungerar:

  • I routningsmallen "default" är routningsparametern culture till vänster om controller, så ändringar i kommer inte att controller ogiltigförklara culture.
  • "blog" I vägmallen culture anses vägparametern vara till höger om controller, som visas i de obligatoriska värdena.

Konfigurera slutpunktsmetadata

Följande länkar innehåller information om hur du konfigurerar slutpunktsmetadata:

Värdmatchning i rutter med RequireHost

RequireHost tillämpar en begränsning på den rutt som kräver den angivna värden. Parametern RequireHost eller [Värd] kan vara:

  • Värd: www.domain.com, matchar www.domain.com med valfri port.
  • Värd med jokertecken: *.domain.com, matchar www.domain.com, subdomain.domain.com, eller www.subdomain.domain.com på valfri port.
  • Port: *:5000, matchar port 5000 med valfri värd.
  • Värd och port: www.domain.com:5000 eller *.domain.com:5000, matchar värd och port.

Flera parametrar kan anges med RequireHost eller [Host]. Villkoret matchar värdar som är giltiga för någon av parametrarna. Matchar till exempel [Host("domain.com", "*.domain.com")], domain.com, www.domain.com, och subdomain.domain.com.

Följande kod använder RequireHost för att kräva den angivna värden på rutten:

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

Följande kod använder [Host] attributet på kontrollanten för att kräva någon av de angivna värdarna:

[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] När attributet tillämpas på både kontrollanten och åtgärdsmetoden:

  • Attributet för åtgärden används.
  • Kontrollantattributet ignoreras.

Prestandavägledning för routning

Merparten av routningen uppdaterades i ASP.NET Core 3.0 för att öka prestandan.

När en app har prestandaproblem misstänks routning ofta vara problemet. Anledningen till att routning misstänks är att ramverk som kontrollers och Razor Pages rapporterar hur lång tid som spenderas inom ramverket i sina loggningsmeddelanden. När det finns en betydande skillnad mellan den tid som rapporteras av kontrollanter och den totala tiden för begäran:

  • Utvecklare eliminerar sin appkod som källa till problemet.
  • Det är vanligt att anta att routning är orsaken.

Routning är prestandatestad med tusentals slutpunkter. Det är osannolikt att en typisk app kommer att stöta på ett prestandaproblem bara genom att vara för stor. Den vanligaste grundorsaken till långsamma routningsprestanda är vanligtvis ett dåligt fungerande anpassad middleware.

Följande kodexempel visar en grundläggande teknik för att begränsa fördröjningskällan:

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

För att schemalägga dirigeringen:

  • Alternativt infoga mellanprogrammen med en kopia av tidsmellanprogrammet som visas i föregående kod.
  • Lägg till en unik identifierare för att korrelera tidsdata med koden.

Det här är ett grundläggande sätt att begränsa fördröjningen när den är betydande, till exempel mer än 10ms. Subtrahera Time 2 från Time 1 rapporterar tiden som spenderats i UseRouting mellanprogrammet.

Följande kod använder en mer kompakt metod för föregående tidskod:

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

Potentiellt dyra routningsfunktioner

Följande lista ger en inblick i routningsfunktioner som är relativt dyra jämfört med grundläggande routningsmallar:

  • Reguljära uttryck: Det går att skriva reguljära uttryck som är komplexa eller har lång körningstid med en liten mängd indata.
  • Komplicerade segment ({x}-{y}-{z}):
    • Är betydligt dyrare än att parsa ett vanligt URL-sökvägssegment.
    • Resulterar i att många fler delsträngar kommer att allokeras.
    • Logiken för komplext segment uppdaterades inte i ASP.NET Core 3.0-routningsprestandauppdatering.
  • Synkron dataåtkomst: Många komplexa appar har databasåtkomst som en del av routningen. ASP.NET Core 2.2 eller tidigare routning kanske inte ger rätt utökningspunkter som stöd för routning av databasåtkomst. Till exempel IRouteConstraint, och IActionConstraint är synkrona. Utökningspunkter som MatcherPolicy och EndpointSelectorContext är asynkrona.

Vägledning för biblioteksförfattare

Det här avsnittet innehåller vägledning för biblioteksförfattare som bygger ovanpå routning. Den här informationen är avsedd att säkerställa att apputvecklare har en bra upplevelse med hjälp av bibliotek och ramverk som utökar routningen.

Definiera slutpunkter

Om du vill skapa ett ramverk som använder routning för URL-matchning börjar du med att definiera en användarupplevelse som bygger ovanpå UseEndpoints.

BYGG ovanpå IEndpointRouteBuilder. På så sätt kan användarna skapa ditt ramverk med andra ASP.NET Core-funktioner utan förväxling. Varje ASP.NET Core-mall innehåller routning. Anta att routning är närvarande och bekant för användare.

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...);

    endpoints.MapHealthChecks("/healthz");
});

Returnera en förseglad betongtyp från ett anrop till MapMyFramework(...) som implementerar IEndpointConventionBuilder. De flesta ramverksmetoder Map... följer det här mönstret. Gränssnittet IEndpointConventionBuilder :

  • Tillåter sammansättning av metadata.
  • Är mål för en mängd olika tilläggsmetoder.

Om du deklarerar din egen typ kan du lägga till dina egna ramverksspecifika funktioner i byggaren. Det är okej att omsluta ett ramverksdeklarerat byggobjekt och vidarebefordra anrop till det.

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...).RequireAuthorization()
                                 .WithMyFrameworkFeature(awesome: true);

    endpoints.MapHealthChecks("/healthz");
});

ÖVERVÄG att skriva din egen EndpointDataSource. EndpointDataSource är en grundläggande konstruktion för att deklarera och uppdatera en samling slutpunkter. EndpointDataSource är ett kraftfullt API som används av kontrollanter och Razor sidor.

Routningstesterna har ett grundläggande exempel på en datakälla som inte uppdateras.

Försök INTE att registrera en EndpointDataSource som standard. Kräv att användare registrerar ditt ramverk i UseEndpoints. Routningsfilosofin är att ingenting ingår som standard, och det UseEndpoints är platsen där slutpunkter ska registreras.

Skapa routningsintegrerade mellanprogram

ÖVERVÄG att definiera metadatatyper som ett gränssnitt.

Gör det möjligt att använda metadatatyper som ett attribut för klasser och metoder.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

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

Ramverk som kontrollanter och Razor sidor stöder tillämpning av metadataattribut på typer och metoder. Om du deklarerar metadatatyper:

  • Gör dem tillgängliga som attribut.
  • De flesta användare är bekanta med att tillämpa attribut.

Om du deklarerar en metadatatyp som ett gränssnitt läggs ett annat flexibelt lager till:

  • Gränssnitt är sammansättningsbara.
  • Utvecklare kan deklarera sina egna typer som kombinerar flera principer.

Gör det möjligt att åsidosätta metadata, som du ser i följande exempel:

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

Det bästa sättet att följa dessa riktlinjer är att undvika att definiera markörmetadata:

  • Leta inte bara efter förekomsten av en metadatatyp.
  • Definiera en egenskap för metadata och kontrollera egenskapen.

Metadatasamlingen är ordnad och möjliggör att överskrida efter prioritet. När det gäller kontrollanter är metadata för åtgärdsmetoden mest specifika.

Gör mellanprogram användbara med och utan routning.

app.UseRouting();

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

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...).RequireAuthorization();
});

Som ett exempel på den här riktlinjen bör du överväga UseAuthorization mellanprogrammet. Med mellanprogrammet för auktorisering kan du ange en reservpolicy. Om den anges, gäller återställningsprincipen för båda:

  • Slutpunkter utan en angiven policy.
  • Begäranden som inte överensstämmer med en slutpunkt.

Detta gör auktoriseringsmellanprogrammet användbart utanför routningskontexten. Mellanprogrammet för auktorisering kan användas för traditionell mellanprogramprogrammering.

Felsökningsdiagnostik

För detaljerade routningsdiagnostikutdata anger du Logging:LogLevel:Microsoft till Debug. I utvecklingsmiljön anger du loggnivån i appsettings.Development.json:

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