Delen via


Routering in ASP.NET Core

Door Ryan Nowak, Kirk Larkin en Rick Anderson

Note

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikel voor de huidige release.

Warning

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie het .NET- en .NET Core-ondersteuningsbeleid voor meer informatie. Zie de .NET 9-versie van dit artikel voor de huidige release.

Important

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikel voor de huidige release.

Routering is verantwoordelijk voor het koppelen van binnenkomende HTTP-aanvragen en het verzenden van deze aanvragen naar de uitvoerbare eindpunten van de app. Eindpunten zijn de eenheden van uitvoerbare aanvraagafhandelingscode van de app. Eindpunten worden gedefinieerd in de app en geconfigureerd wanneer de app wordt gestart. Het overeenkomende eindpuntproces kan waarden extraheren uit de URL van de aanvraag en deze waarden opgeven voor het verwerken van aanvragen. Met behulp van eindpuntgegevens van de app kan routering ook URL's genereren die zijn toegewezen aan eindpunten.

Apps kunnen routering configureren met behulp van:

In dit artikel worden details van ASP.NET Core-routering op laag niveau beschreven. Voor informatie over het configureren van routering:

Routing basics

De volgende code toont een basisvoorbeeld van routering:

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

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

app.Run();

Het voorgaande voorbeeld bevat één eindpunt met behulp van de MapGet methode:

  • Wanneer een HTTP-aanvraag GET naar de hoofd-URL /wordt verzonden:
    • De gemachtigde van de aanvraag wordt uitgevoerd.
    • Hello World! wordt naar het HTTP-antwoord geschreven.
  • Als de aanvraagmethode niet GET is of de hoofd-URL niet /is, worden er geen routeovereenkomsten en wordt een HTTP 404 geretourneerd.

Routering maakt gebruik van een paar middleware, geregistreerd door UseRouting en UseEndpoints:

  • UseRouting voegt routekoppeling toe aan de middleware-pijplijn. Deze middleware bekijkt de set eindpunten die in de app zijn gedefinieerd en selecteert de beste overeenkomst op basis van de aanvraag.
  • UseEndpoints voegt eindpuntuitvoering toe aan de middleware-pijplijn. Het voert de delegate uit die is gekoppeld aan het geselecteerde eindpunt.

Apps hoeven meestal niet UseRouting of UseEndpoints aan te roepen. WebApplicationBuilder configureert een middleware-pijplijn die middleware die is toegevoegd in Program.cs omhult met UseRouting en UseEndpoints. Apps kunnen echter de volgorde wijzigen waarin UseRouting en UseEndpoints worden uitgevoerd door deze methoden expliciet aan te roepen. Met de volgende code wordt bijvoorbeeld een expliciete aanroep uitgevoerd naar UseRouting:

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

app.UseRouting();

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

In de voorgaande code:

  • Het aanroepen van app.Use registreert een aangepaste middleware die aan het begin van de pijplijn wordt uitgevoerd.
  • De aanroep voor UseRouting het configureren van de route die overeenkomt met middleware die moet worden uitgevoerd na de aangepaste middleware.
  • Het eindpunt dat is geregistreerd bij MapGet wordt aan het einde van de pijplijn uitgevoerd.

Als er in het voorgaande voorbeeld geen aanroep van UseRouting was opgenomen, zou de aangepaste middleware worden uitgevoerd na de route-matchende middleware.

Opmerking: routes die rechtstreeks aan de WebApplication worden toegevoegd, worden uitgevoerd aan het einde van de pijplijn.

Endpoints

De MapGet methode wordt gebruikt om een eindpunt te definiëren. Een eindpunt is iets dat het volgende kan zijn:

  • Geselecteerd door de URL en HTTP-methode te koppelen.
  • Uitgevoerd door de gemachtigde uit te voeren.

Eindpunten die door de app kunnen worden vergeleken en uitgevoerd, worden geconfigureerd in UseEndpoints. MapGet MapPost Vergelijkbare methoden verbinden aanvraagafgevaardigden met het routeringssysteem. Aanvullende methoden kunnen worden gebruikt om ASP.NET Core-frameworkfuncties te verbinden met het routeringssysteem:

In het volgende voorbeeld ziet u routering met een geavanceerdere routesjabloon:

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

De tekenreeks /hello/{name:alpha} is een routesjabloon. Er wordt een routesjabloon gebruikt om te configureren hoe het eindpunt wordt gekoppeld. In dit geval komt de sjabloon overeen met:

  • Een URL zoals /hello/Docs
  • Elk URL-pad dat begint met /hello/ gevolgd door een reeks alfabetische tekens. :alpha past een routebeperking toe die alleen overeenkomt met alfabetische tekens. Routebeperkingen worden verderop in dit artikel uitgelegd.

Het tweede segment van het URL-pad: {name:alpha}

In het volgende voorbeeld ziet u routering met statuscontroles en autorisatie:

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

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

In het voorgaande voorbeeld ziet u hoe:

  • De middleware voor autorisatie kan worden gebruikt met routering.
  • Eindpunten kunnen worden gebruikt om autorisatiegedrag te configureren.

De MapHealthChecks aanroep voegt een statuscontrole-eindpunt toe. Door RequireAuthorization aan deze oproep toe te voegen, wordt een autorisatiebeleid aan het eindpunt toegevoegd.

Door UseAuthentication en UseAuthorization aan te roepen, wordt de verificatie- en autorisatie-middleware toegevoegd. Deze middleware worden geplaatst tussen UseRouting en UseEndpoints zodat ze het volgende kunnen:

  • Zien welk eindpunt is geselecteerd door UseRouting.
  • Pas een autorisatiebeleid toe voordat UseEndpoints naar het eindpunt wordt verzonden.

Endpoint metadata

In het voorgaande voorbeeld zijn er twee eindpunten, maar alleen het eindpunt voor de statuscontrole heeft een autorisatiebeleid gekoppeld. Als de aanvraag overeenkomt met het eindpunt van de statuscontrole, /healthzwordt er een autorisatiecontrole uitgevoerd. Dit laat zien dat er extra gegevens aan eindpunten kunnen worden gekoppeld. Deze extra gegevens worden eindpuntmetagegevens genoemd:

  • De metagegevens kunnen worden verwerkt door middel van routeringsbewuste middleware.
  • De metagegevens kunnen van elk .NET-type zijn.

Routing concepts

Het routeringssysteem bouwt voort op de middleware-pijplijn door het krachtige eindpuntconcept toe te voegen. Eindpunten vertegenwoordigen eenheden van de functionaliteit van de app die van elkaar verschillen wat betreft routering, autorisatie en een willekeurig aantal ASP.NET Core-systemen.

ASP.NET Kerneindpuntdefinitie

Een ASP.NET Core-eindpunt is:

De volgende code laat zien hoe u het eindpunt ophaalt en inspecteert dat overeenkomt met de huidige aanvraag:

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

Het eindpunt, indien geselecteerd, kan worden opgehaald uit de HttpContext. De eigenschappen kunnen worden geïnspecteerd. Eindpuntobjecten zijn onveranderbaar en kunnen niet worden gewijzigd na het maken. Het meest voorkomende type eindpunt is een RouteEndpoint. RouteEndpoint bevat informatie waarmee deze kan worden geselecteerd door het routeringssysteem.

In de voorgaande code configureert app.Use een inline-middleware.

In de volgende code blijkt dat er, afhankelijk van waar in de pijplijn app.Use wordt aangeroepen, mogelijk geen eindpunt is.

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

In het voorgaande voorbeeld worden instructies toegevoegd Console.WriteLine die aangeven of er al dan niet een eindpunt is geselecteerd. Ter duidelijkheid wijst het voorbeeld een weergavenaam toe aan het opgegeven / eindpunt.

Het voorgaande voorbeeld bevat ook aanroepen naar UseRouting en UseEndpoints om precies te bepalen wanneer deze middleware in de pijplijn wordt uitgevoerd.

Als u deze code uitvoert met een URL van /, wordt:

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

Als u deze code uitvoert met een andere URL, wordt het volgende weergegeven:

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

Deze uitvoer laat zien dat:

  • Het eindpunt is altijd null voordat UseRouting wordt aangeroepen.
  • Als er een overeenkomst wordt gevonden, is het eindpunt niet null tussen UseRouting en UseEndpoints.
  • De UseEndpoints middleware is terminal wanneer er een overeenkomst wordt gevonden. Terminal middleware wordt verderop in dit artikel gedefinieerd.
  • Na UseEndpoints wordt de middleware alleen uitgevoerd als er geen overeenkomst wordt gevonden.

De UseRouting middleware gebruikt de SetEndpoint methode om het eindpunt aan de huidige context te koppelen. Het is mogelijk om de UseRouting middleware te vervangen door aangepaste logica en nog steeds de voordelen te krijgen van het gebruik van eindpunten. Eindpunten zijn een laag-niveau primitief zoals middleware en zijn niet gekoppeld aan de routeringsimplementatie. De meeste apps hoeven UseRouting niet te vervangen door aangepaste logica.

De UseEndpoints middleware is ontworpen om te worden gebruikt in combinatie met de UseRouting middleware. De kernlogica voor het uitvoeren van een eindpunt is niet ingewikkeld. Gebruik GetEndpoint om het eindpunt op te halen en roep vervolgens de eigenschap van RequestDelegate aan.

De volgende code laat zien hoe middleware invloed kan hebben op of kan reageren op routering:

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

In het voorgaande voorbeeld ziet u twee belangrijke concepten:

  • Middleware kan worden uitgevoerd voordat UseRouting om de gegevens waarop routering plaatsvindt te wijzigen.
  • Middleware kan worden uitgevoerd tussen UseRouting en UseEndpoints om de resultaten van routering te verwerken voordat het eindpunt wordt uitgevoerd.
    • Middleware die wordt uitgevoerd tussen UseRouting en UseEndpoints:
      • Inspecteert meestal metagegevens om inzicht te hebben in de eindpunten.
      • Vaak neemt u beveiligingsbeslissingen, zoals gedaan door UseAuthorization en UseCors.
    • Met de combinatie van middleware en metagegevens kunt u beleidsregels per eindpunt configureren.

In de voorgaande code ziet u een voorbeeld van een aangepaste middleware die beleid per eindpunt ondersteunt. De middleware schrijft een auditlog van de toegang tot gevoelige gegevens naar de console. De middleware kan worden geconfigureerd om een eindpunt te controleren met de RequiresAuditAttribute metagegevens. In dit voorbeeld ziet u een opt-in-patroon waarbij alleen eindpunten die als gevoelig zijn gemarkeerd, worden gecontroleerd. Het is mogelijk om deze logica omgekeerd te definiëren en alles te controleren dat niet als veilig is gemarkeerd, bijvoorbeeld. Het eindpuntmetagegevenssysteem is flexibel. Deze logica kan op welke manier dan ook worden ontworpen voor de use-case.

De voorgaande voorbeeldcode is bedoeld om de basisconcepten van eindpunten te demonstreren. Het voorbeeld is niet bedoeld voor productiegebruik. Een volledigere versie van een middleware voor auditlogboeken zou het volgende doen:

  • Meld u aan bij een bestand of database.
  • Neem details op, zoals de gebruiker, het IP-adres, de naam van het gevoelige eindpunt en meer.

De metagegevens RequiresAuditAttribute van het controlebeleid worden gedefinieerd als een Attribute voor eenvoudiger gebruik met frameworks op basis van klassen, zoals controllers en SignalR. Wanneer u route naar code gebruikt:

  • Metagegevens worden gekoppeld aan een builder-API.
  • Frameworks op basis van klassen bevatten alle kenmerken van de bijbehorende methode en klasse bij het maken van eindpunten.

De aanbevolen procedures voor metagegevenstypen zijn om ze te definiëren als interfaces of kenmerken. Interfaces en kenmerken staan hergebruik van code toe. Het metagegevenssysteem is flexibel en legt geen beperkingen op.

Terminal-middleware vergelijken met routering

In het volgende voorbeeld ziet u zowel terminal-middleware als routering:

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

De stijl van middleware die wordt weergegeven met Approach 1: is terminal-middleware. Dit wordt terminal-middleware genoemd omdat er een overeenkomende bewerking wordt uitgevoerd:

  • De overeenkomende bewerking in het voorgaande voorbeeld is Path == "/" voor de middleware en Path == "/Routing" voor routering.
  • Wanneer een overeenkomst succesvol is, wordt bepaalde functionaliteit uitgevoerd en wordt er teruggekeerd zonder dat de next middleware wordt aangeroepen.

Dit wordt terminal middleware genoemd omdat de zoekopdracht wordt beëindigd, bepaalde functionaliteit wordt uitgevoerd en vervolgens wordt geretourneerd.

De volgende lijst vergelijkt terminal-middleware met routering:

  • Beide benaderingen maken het beëindigen van de verwerkingspijplijn mogelijk:
    • Middleware beëindigt de pijplijn door te retourneren in plaats van aan te nextroepen.
    • Eindpunten zijn altijd terminal.
  • Met terminal-middleware kunt u de middleware op een willekeurige plaats in de pijplijn plaatsen:
    • Eindpunten worden uitgevoerd op de positie van UseEndpoints.
  • Met terminal-middleware kan willekeurige code worden bepaald wanneer de middleware overeenkomt:
    • Aangepaste routingcode kan langdradig zijn en moeilijk correct te schrijven.
    • Routering biedt eenvoudige oplossingen voor typische apps. Voor de meeste apps is geen aangepaste routekoppelingscode vereist.
  • Eindpuntinterface met middleware zoals UseAuthorization en UseCors.
    • Het gebruik van een terminal-middleware met UseAuthorization of UseCors vereist handmatige interfacing met het autorisatiesysteem.

Een eindpunt definieert beide:

  • Een gemachtigde voor het verwerken van aanvragen.
  • Een verzameling willekeurige metagegevens. De metagegevens worden gebruikt om kruislingse problemen te implementeren op basis van beleidsregels en configuratie die aan elk eindpunt zijn gekoppeld.

Terminal-middleware kan een effectief hulpprogramma zijn, maar kan het volgende vereisen:

  • Een aanzienlijke hoeveelheid coderen en testen.
  • Handmatige integratie met andere systemen om het gewenste flexibiliteitsniveau te bereiken.

Overweeg om te integreren met routering voordat u een terminal-middleware schrijft.

Bestaande terminal-middleware die kan worden geïntegreerd met Kaart of MapWhen kan meestal worden omgezet in een routeringsbewust eindpunt. MapHealthChecks demonstreert het patroon voor router-ware:

  • Schrijf een extensiemethode op IEndpointRouteBuilder.
  • Maak een geneste middleware-pijplijn met behulp van CreateApplicationBuilder.
  • Koppel de middleware aan de nieuwe pijplijn. In dit geval UseHealthChecks.
  • Build de middleware-pijplijn naar een RequestDelegate.
  • Roep Map aan en geef de nieuwe middleware-pijplijn op.
  • Het opbouwobject dat door de extensiemethode Map wordt geleverd, wordt geretourneerd.

De volgende code toont het gebruik van MapHealthChecks:

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

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

In het voorgaande voorbeeld ziet u waarom het retourneren van het opbouwobject belangrijk is. Als het opbouwobject wordt geretourneerd, kan de app-ontwikkelaar beleidsregels configureren, zoals autorisatie voor het eindpunt. In dit voorbeeld heeft de gezondheidscontroles middleware geen directe integratie met het autorisatiesysteem.

Het metagegevenssysteem is gemaakt als reactie op de problemen die zijn opgetreden door auteurs van uitbreidbaarheid met behulp van terminal-middleware. Het is problematisch voor elke middleware om een eigen integratie met het autorisatiesysteem te implementeren.

URL matching

  • Is het proces waarmee routering overeenkomt met een binnenkomende aanvraag naar een eindpunt.
  • Is gebaseerd op gegevens in het URL-pad en de headers.
  • Kan worden uitgebreid om rekening te houden met alle gegevens in de aanvraag.

Wanneer een routing-middleware wordt uitgevoerd, worden een Endpoint en routewaarden ingesteld op een verzoekfunctie van het HttpContext huidige verzoek:

Middleware die wordt uitgevoerd nadat de routerings-middleware het eindpunt kan inspecteren en actie kan ondernemen. Een autorisatie-middleware kan bijvoorbeeld de metagegevensverzameling van het eindpunt ondervragen voor een autorisatiebeleid. Nadat alle middleware in de pijplijn voor aanvraagverwerking is uitgevoerd, wordt de gemachtigde van het geselecteerde eindpunt aangeroepen.

Het routeringssysteem in eindpuntroutering is verantwoordelijk voor alle verzendbeslissingen. Omdat de middleware beleidsregels toepast op basis van het geselecteerde eindpunt, is het belangrijk dat:

  • Elke beslissing die van invloed kan zijn op verzending of de toepassing van beveiligingsbeleid wordt gemaakt in het routeringssysteem.

Warning

Voor achterwaartse compatibiliteit, wanneer een controller- of Razor pagina's eindpuntdelegatie wordt uitgevoerd, worden de eigenschappen van RouteContext.RouteData ingesteld op de juiste waarden op basis van de verwerking van de aanvraag tot nu toe.

Het RouteContext type wordt gemarkeerd als verouderd in een toekomstige release:

  • Migreren RouteData.Values naar HttpRequest.RouteValues.
  • Migreer RouteData.DataTokens om IDataTokensMetadata uit de eindpuntmetagegevens op te halen.

URL-vergelijking werkt in een configureerbare set fasen. In elke fase is de uitvoer een verzameling van overeenkomsten. De reeks wedstrijden kan verder worden beperkt door de volgende fase. De implementatie van routering garandeert geen verwerkingsvolgorde voor overeenkomende eindpunten. Alle mogelijke overeenkomsten worden in één keer verwerkt. De fasen van het overeenkomen van de URL vinden plaats in de volgende volgorde. ASP.NET Core:

  1. Verwerkt het URL-pad op basis van de set eindpunten en de bijbehorende routesjablonen, en verzamelt alle overeenkomsten.
  2. Neemt de voorgaande lijst, en verwijdert overeenkomsten die niet voldoen aan de toegepaste routebeperkingen.
  3. Neemt de voorgaande lijst en verwijdert overeenkomsten die falen in de set MatcherPolicy instanties.
  4. EndpointSelector Hiermee kunt u een definitieve beslissing nemen uit de voorgaande lijst.

De lijst met eindpunten wordt gerangschikt op basis van:

Alle overeenkomende eindpunten worden in elke fase verwerkt totdat het EndpointSelector is bereikt. Het EndpointSelector is de laatste fase. Het systeem kiest het eindpunt met de hoogste prioriteit van de overeenkomsten als beste overeenkomst. Als er andere overeenkomsten zijn met dezelfde prioriteit als de beste overeenkomst, wordt er een dubbelzinnige match-exceptie opgeworpen.

De routeprioriteit wordt berekend op basis van een specifiekere routesjabloon die een hogere prioriteit krijgt. Denk bijvoorbeeld aan de sjablonen /hello en /{message}:

  • Beide komen overeen met het URL-pad /hello.
  • /hello is specifieker en dus hogere prioriteit.

Over het algemeen doet routeprioriteit goed werk bij het kiezen van de beste match voor de soorten URL-schema's die in de praktijk worden gebruikt. Gebruik Order deze functie alleen wanneer dat nodig is om dubbelzinnigheid te voorkomen.

Vanwege de soorten uitbreidbaarheid die wordt geboden door routering, is het niet mogelijk dat het routeringssysteem van tevoren de ambigu routes berekent. Bekijk een voorbeeld zoals de routesjablonen /{message:alpha} en /{message:int}:

  • De alpha beperking komt alleen overeen met alfabetische tekens.
  • De int beperking komt alleen overeen met getallen.
  • Deze sjablonen hebben dezelfde routeprioriteit, maar er is geen enkele URL die beide overeenkomen.
  • Als het routeringssysteem bij het opstarten een dubbelzinnigheidsfout heeft gerapporteerd, wordt deze geldige use-case geblokkeerd.

Warning

De volgorde van bewerkingen binnen UseEndpoints heeft geen invloed op het gedrag van routering, met één uitzondering. MapControllerRoute en MapAreaRoute wijs automatisch een orderwaarde toe aan hun eindpunten op basis van de volgorde die ze worden aangeroepen. Dit simuleert langdurige gedrag van controllers zonder dat het routeringssysteem dezelfde garanties biedt als oudere routeringsimplementaties.

Eindpuntroutering in ASP.NET Core:

  • Heeft geen concept van routes.
  • Biedt geen bestelgaranties. Alle eindpunten worden tegelijk verwerkt.

Prioriteit van routesjabloon en volgorde van eindpuntselectie

Prioriteit van routesjabloon is een systeem dat elke routesjabloon een waarde toewijst op basis van hoe specifiek het is. Prioriteit van routesjabloon:

  • Vermijdt de noodzaak om de volgorde van eindpunten in veelvoorkomende gevallen aan te passen.
  • Pogingen om overeen te komen met de algemene verwachtingen van routeringsgedrag.

Denk bijvoorbeeld aan sjablonen /Products/List en /Products/{id}. Het zou redelijk zijn om ervan uit te gaan dat /Products/List een betere overeenkomst is dan /Products/{id} voor het URL-pad /Products/List. Dit werkt omdat het letterlijke segment /List wordt beschouwd als een betere prioriteit dan het parametersegment /{id}.

De details van hoe prioriteit werkt, zijn gekoppeld aan de wijze waarop routesjablonen worden gedefinieerd:

  • Sjablonen met meer segmenten worden als specifieker beschouwd.
  • Een segment met letterlijke tekst wordt beschouwd als specifieker dan een parametersegment.
  • Een parametersegment met een beperking wordt beschouwd als specifieker dan één zonder.
  • Een complex segment wordt beschouwd als even specifiek als een parametersegment met een beperking.
  • Catch-all-parameters zijn het minst specifiek. Zie catch-all in de sectie Routesjablonen voor belangrijke informatie over catch-all routes.

Concepten voor het genereren van URL's

URL generation:

  • Is het proces waarmee routering een URL-pad kan maken op basis van een set routewaarden.
  • Hiermee is een logische scheiding mogelijk tussen eindpunten en de URL's die er toegang toe hebben.

Eindpuntroutering omvat de LinkGenerator API. LinkGenerator is een singleton-service die beschikbaar is via DI. De LinkGenerator API kan buiten de context van een uitvoeringsaanvraag worden gebruikt. Mvc.IUrlHelper en scenario's die afhankelijk zijn IUrlHelpervan, zoals Tag Helpers, HTML Helpers en Actieresultaten, gebruiken de LinkGenerator API intern om mogelijkheden voor het genereren van koppelingen te bieden.

De koppelingsgenerator wordt ondersteund door het concept van adres- en adresschema's. Een adresschema is een manier om de eindpunten te bepalen die moeten worden overwogen voor het genereren van koppelingen. De scenario's van routenaam en routewaarden die voor veel gebruikers bekend zijn, worden met controllers en Razor Pagina's geïmplementeerd als een adresschema.

De koppelingsgenerator kan via de volgende uitbreidingsmethoden een koppeling maken naar controllers en Razor pagina's:

Overbelastingen van deze methoden accepteren argumenten die de HttpContext. Deze methoden zijn functioneel gelijkwaardig aan Url.Action en Url.Page, maar bieden extra flexibiliteit en opties.

De GetPath* methoden zijn het meest vergelijkbaar met Url.Action en Url.Page, omdat ze een URI genereren die een absoluut pad bevat. De GetUri* methoden genereren altijd een absolute URI die een schema en host bevat. De methoden die een HttpContext accepteren, genereren een URI in de context van de lopende aanvraag. De omgevingsroutewaarden , het URL-basispad, het schema en de host van de uitvoerbare aanvraag worden gebruikt, tenzij deze worden overschreven.

LinkGenerator kan worden aangeroepen met een adres. Het genereren van een URI vindt plaats in twee stappen:

  1. Een adres is gebonden aan een lijst met eindpunten die overeenkomen met het adres.
  2. De eindpunten RoutePattern worden geëvalueerd totdat een routepatroon dat overeenkomt met de opgegeven waarden wordt gevonden. De resulterende uitvoer wordt gecombineerd met de andere URI-onderdelen die aan de koppelingsgenerator worden geleverd en geretourneerd.

De methoden die door LinkGenerator worden geboden, ondersteunen het genereren van standaardkoppelingen voor elk type adres. De handigste manier om de koppelingsgenerator te gebruiken, is via extensiemethoden die bewerkingen uitvoeren voor een specifiek adrestype:

Extension Method Description
GetPathByAddress Genereert een URI met een absoluut pad op basis van de opgegeven waarden.
GetUriByAddress Genereert een absolute URI op basis van de opgegeven waarden.

Warning

Let op de volgende gevolgen van het aanroepen LinkGenerator van methoden:

  • Gebruik GetUri* extensiemethoden met voorzichtigheid in een app-configuratie die de Host header van binnenkomende aanvragen niet valideert. Als de Host header van binnenkomende aanvragen niet wordt gevalideerd, kan niet-vertrouwde aanvraaginvoer worden teruggestuurd naar de client in URI's in een weergave of pagina. Het is raadzaam dat alle productie-apps hun server configureren om de Host header te valideren op basis van bekende geldige waarden.

  • Wees LinkGenerator voorzichtig met middleware in combinatie met Map of MapWhen. Map* wijzigt het basispad van de uitvoerende aanvraag, wat van invloed is op de uitvoer van linkgeneratie. Alle API's maken het mogelijk om LinkGenerator een basispad op te geven. Geef een leeg basispad op om het effect bij het Map* genereren van koppelingen ongedaan te maken.

Middleware example

In het volgende voorbeeld gebruikt een middleware de LinkGenerator API om een koppeling te maken naar een actiemethode met een lijst met winkelproducten. Het gebruik van de koppelingsgenerator door deze in een klasse te injecteren en aanroepen GenerateLink is beschikbaar voor elke klasse in een 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.");
    }
}

Route templates

Tokens binnen {} definiëren routeparameters die worden gebonden als de route overeenkomt. Er kunnen meer dan één routeparameter worden gedefinieerd in een routesegment, maar routeparameters moeten worden gescheiden door een letterlijke waarde. For example:

{controller=Home}{action=Index}

is geen geldige route, omdat er geen numerieke waarde tussen {controller} en {action} staat. Routeparameters moeten een naam hebben en kunnen aanvullende kenmerken hebben opgegeven.

Letterlijke tekst anders dan routeparameters (bijvoorbeeld {id}) en het padscheidingsteken / moet overeenkomen met de tekst in de URL. Tekstvergelijking is niet hoofdlettergevoelig en is gebaseerd op de gedecodeerde weergave van het pad van de URL. Als u een letterlijk routeparameterscheidingsteken { wilt vergelijken of }, escapet u het scheidingsteken door het teken te herhalen. Bijvoorbeeld {{ of }}.

Sterretje * of dubbel sterretje **:

  • Kan worden gebruikt als voorvoegsel voor een routeparameter om verbinding te maken met de rest van de URI.
  • Worden een catch-all-parameters genoemd. Bijvoorbeeld: blog/{**slug}
    • Komt overeen met elke URI die begint met blog/ en die een waarde bevat die erop volgt.
    • De waarde die volgt op blog/ wordt toegewezen aan de slug routewaarde.

Warning

Een catch-all--parameter kan verkeerde routes matchen door een fout in de routering. Apps die door deze fout worden beïnvloed, hebben de volgende kenmerken:

  • Een algemene route, bijvoorbeeld {**slug}"
  • De catch-all-route kan niet overeenkomen met aanvragen die moeten overeenkomen.
  • Als u andere routes verwijdert, begint de catch-all route te werken.

Zie GitHub-bugs 18677 en 16579 voor voorbeeldgevallen die deze bug ervaren.

Een opt-in-oplossing voor deze fout is opgenomen in .NET Core 3.1.301 of hoger SDK. Met de volgende code wordt een interne switch ingesteld waarmee deze fout wordt opgelost:

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

Catch-all-parameters kunnen ook overeenkomen met de lege tekenreeks.

De catch-all-parameter escapeert de juiste tekens wanneer de route wordt gebruikt om een URL te genereren, inclusief padscheidingstekens /. De route foo/{*path} met routewaarden { path = "my/path" } genereert foo/my%2Fpathbijvoorbeeld . Noteer de escaped slash. Gebruik het voorvoegsel van de ** routeparameter om het padscheidingsteken af te ronden. De route foo/{**path} met { path = "my/path" } genereert foo/my/path.

URL-patronen die proberen een bestandsnaam met een optionele bestandsextensie vast te leggen, hebben aanvullende overwegingen. Denk bijvoorbeeld aan de sjabloon files/{filename}.{ext?}. Wanneer waarden voor beide filename en ext bestaan, worden beide waarden ingevuld. Als er slechts een waarde voor filename bestaat in de URL, komt de route overeen omdat het volgtraject . optioneel is. De volgende URL's komen overeen met deze route:

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

Routeparameters kunnen standaardwaarden hebben die zijn aangewezen door de standaardwaarde op te geven na de parameternaam gescheiden door een gelijkteken (=). Definieert {controller=Home} bijvoorbeeld Home als de standaardwaarde voor controller. De standaardwaarde wordt gebruikt als er geen waarde aanwezig is in de URL voor de parameter. Routeparameters worden optioneel gemaakt door een vraagteken (?) toe te voegen aan het einde van de parameternaam. Bijvoorbeeld: id?. Het verschil tussen optionele waarden en standaardrouteparameters is:

  • Een routeparameter met een standaardwaarde produceert altijd een waarde.
  • Een optionele parameter heeft alleen een waarde wanneer een waarde wordt opgegeven door de aanvraag-URL.

Routeparameters kunnen beperkingen hebben die overeenkomen met de routewaarde die is gebonden aan de URL. Door : en de naam van de beperking na de naam van de routeparameter toe te voegen, wordt een inline beperking voor een routeparameter gespecificeerd. Als voor de beperking argumenten zijn vereist, staan deze tussen haakjes (...) achter de naam van de beperking. Er kunnen meerdere inlinebeperkingen worden opgegeven door een andere : naam en beperking toe te voegen.

De naam en argumenten van de beperking worden aan de IInlineConstraintResolver-service doorgegeven om een exemplaar van IRouteConstraint te maken, voor gebruik in URL-verwerking. De routesjabloon blog/{article:minlength(10)} geeft bijvoorbeeld een minlength beperking op met het argument 10. Zie de sectie Routebeperkingen voor meer informatie over routebeperkingen en een lijst met de beperkingen die door het framework worden geboden.

Routeparameters kunnen ook parametertransformatoren hebben. Parametertransformatoren transformeren de waarde van een parameter bij het genereren van koppelingen en overeenkomende acties en pagina's naar URL's. Net zoals beperkingen kunnen parametertransformatoren inline aan een routeparameter worden toegevoegd door na de naam van de routeparameter een : en de naam van de transformer toe te voegen. De routesjabloon blog/{article:slugify} geeft bijvoorbeeld een slugify transformator op. Zie de sectie Parametertransformatoren voor meer informatie over parametertransformaties .

In de volgende tabel ziet u voorbeeldroutesjablonen en hun gedrag:

Route Template Voorbeeld van overeenkomende URI De aanvraag-URI...
hello /hello Komt alleen overeen met het ene pad /hello.
{Page=Home} / Koppelt en stelt Page in op Home.
{Page=Home} /Contact Koppelt en stelt Page in op Contact.
{controller}/{action}/{id?} /Products/List Wordt toegewezen aan de Products controller en List actie.
{controller}/{action}/{id?} /Products/Details/123 Wordt toegewezen aan de controller Products en de actie Details, waarbij id is ingesteld op 123.
{controller=Home}/{action=Index}/{id?} / Verwijst naar de Home controller en Index methode. id wordt genegeerd.
{controller=Home}/{action=Index}/{id?} /Products Verwijst naar de Products controller en Index methode. id wordt genegeerd.

Het gebruik van een sjabloon is over het algemeen de eenvoudigste methode voor routering. Beperkingen en standaardwaarden kunnen ook buiten de routesjabloon worden opgegeven.

Complex segments

Complexe segmenten worden verwerkt door letterlijke scheidingstekens van rechts naar links op een niet-gretige wijze te vergelijken. Is bijvoorbeeld [Route("/a{b}c{d}")] een complex segment. Complexe segmenten werken op een bepaalde manier die moet worden begrepen om ze succesvol te kunnen gebruiken. In het voorbeeld in deze sectie ziet u waarom complexe segmenten alleen goed werken wanneer de tekst van het scheidingsteken niet binnen de parameterwaarden wordt weergegeven. Het gebruik van een regex en het handmatig extraheren van de waarden is nodig voor complexere gevallen.

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Dit is een samenvatting van de stappen die routering uitvoert met de sjabloon /a{b}c{d} en het URL-pad /abcd. De | functie wordt gebruikt om te visualiseren hoe het algoritme werkt:

  • De eerste letterlijke waarde, van rechts naar links, is c. Dus /abcd wordt vanaf rechts gezocht en vindt /ab|c|d.
  • Alles aan de rechterkant (d) komt nu overeen met de routeparameter {d}.
  • De volgende letterlijke waarde is a, gelezen van rechts naar links. Dus /ab|c|d wordt gezocht vanaf waar we gebleven waren, dan a wordt gevonden /|a|b|c|d.
  • De waarde aan de rechterkant (b) komt nu overeen met de routeparameter {b}.
  • Er is geen resterende tekst en geen resterende routesjabloon, dus dit komt overeen.

Hier volgt een voorbeeld van een negatief geval met dezelfde sjabloon /a{b}c{d} en het URL-pad /aabcd. De | functie wordt gebruikt om te visualiseren hoe het algoritme werkt. Dit geval is geen overeenkomst, wat wordt uitgelegd door hetzelfde algoritme:

  • De eerste letterlijke waarde, van rechts naar links, is c. Dus /aabcd wordt vanaf rechts gezocht en vindt /aab|c|d.
  • Alles aan de rechterkant (d) komt nu overeen met de routeparameter {d}.
  • De volgende letterlijke waarde is a, gelezen van rechts naar links. Dus /aab|c|d wordt gezocht vanaf waar we gebleven waren, dan a wordt gevonden /a|a|b|c|d.
  • De waarde aan de rechterkant (b) komt nu overeen met de routeparameter {b}.
  • Op dit moment is er nog tekst a, maar het algoritme heeft geen routesjabloon meer om te parseren, dus dit is geen overeenkomst.

Omdat het overeenkomende algoritme niet-graaiend is:

  • Deze komt overeen met de kleinste hoeveelheid tekst die in elke stap mogelijk is.
  • Elk geval waarin de scheidingstekenwaarde in de parameterwaarden wordt weergegeven, resulteert in niet-overeenkomende waarden.

Reguliere expressies bieden veel meer controle over hun overeenkomende gedrag.

Gulzige matching, ook bekend als maximale matching, probeert de langst mogelijke overeenkomst in de invoertekst te vinden die aan het regex-patroon voldoet. Niet-greedy matching, ook wel bekend als luie overeenkomst, zoekt de kortst mogelijke overeenkomst in de invoertekst die voldoet aan het regex-patroon.

Routeren met speciale tekens

Routering met speciale tekens kan leiden tot onverwachte resultaten. Denk bijvoorbeeld aan een controller met de volgende actiemethode:

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

Wanneer string id deze de volgende gecodeerde waarden bevat, kunnen onverwachte resultaten optreden:

ASCII Encoded
/ %2F
+

Routeparameters worden niet altijd gedecodeerd. Dit probleem kan in de toekomst worden opgelost. Zie dit GitHub-probleem voor meer informatie;

Route constraints

Routebeperkingen worden uitgevoerd wanneer er een overeenkomst is opgetreden met de binnenkomende URL en het URL-pad wordt omgezet in routewaarden. Routebeperkingen inspecteren doorgaans de routewaarde die via de routesjabloon is gekoppeld en bepalen of de waarde acceptabel is door een waar of onwaar beslissing te maken. Bij sommige routebeperkingen worden gegevens buiten de routewaarde gebruikt om na te gaan of de aanvraag kan worden gerouteerd. Bijvoorbeeld kan de HttpMethodRouteConstraint een aanvraag accepteren of weigeren op basis van het HTTP-werkwoord. Beperkingen worden gebruikt bij het genereren van routeringsaanvragen en het genereren van koppelingen.

Warning

Gebruik geen beperkingen voor invoervalidatie. Als beperkingen worden gebruikt voor invoervalidatie, resulteert ongeldige invoer in een 404 antwoord niet gevonden. Ongeldige invoer moet een 400 ongeldige aanvraag opleveren met een geschikt foutbericht. Routebeperkingen worden gebruikt om vergelijkbare routes te ontkoppelen, niet om de invoer voor een bepaalde route te valideren.

In de volgende tabel ziet u de standaardroutebeperkingen en het verwachte gedrag:

constraint Example Example Matches Notes
int {id:int} 123456789, -123456789 Komt overeen met een geheel getal
bool {active:bool} true, FALSE Komt overeen met true of false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Komt overeen met een geldige DateTime waarde in de invariante cultuur. Zie de voorgaande waarschuwing
decimal {price:decimal} 49.99, -1,000.01 Komt overeen met een geldige decimal waarde in de invariante cultuur. Zie de voorgaande waarschuwing
double {weight:double} 1.234, -1,001.01e8 Komt overeen met een geldige double waarde in de invariante cultuur. Zie de voorgaande waarschuwing
float {weight:float} 1.234, -1,001.01e8 Komt overeen met een geldige float waarde in de invariante cultuur. Zie de voorgaande waarschuwing
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Komt overeen met een geldige Guid waarde
long {ticks:long} 123456789, -123456789 Komt overeen met een geldige long waarde
minlength(value) {username:minlength(4)} Rick Tekenreeks moet ten minste 4 tekens bevatten
maxlength(value) {filename:maxlength(8)} MyFile Tekenreeks mag niet langer zijn dan 8 tekens
length(length) {filename:length(12)} somefile.txt String moet exact 12 tekens lang zijn
length(min,max) {filename:length(8,16)} somefile.txt Een reeks moet minimaal 8 en niet meer dan 16 tekens lang zijn
min(value) {age:min(18)} 19 De waarde van het gehele getal moet ten minste 18 zijn
max(value) {age:max(120)} 91 Een geheel getal mag niet meer dan 120 zijn
range(min,max) {age:range(18,120)} 91 Een geheel getal moet ten minste 18 zijn, maar niet meer dan 120
alpha {name:alpha} Rick Tekenreeks moet bestaan uit een of meer alfabetische tekens a-z en niet hoofdlettergevoelig
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 De tekenreeks moet overeenkomen met de reguliere expressie. Zie tips voor het definiëren van een reguliere expressie
required {name:required} Rick Wordt gebruikt om af te dwingen dat er een niet-parameterwaarde aanwezig is tijdens het genereren van de URL
file {filename:file} myfile.txt Tekenreeks kan padsegmenten bevatten, maar het laatste segment moet een punt (.) hebben en worden gevolgd door een of meer niet-punttekens
nonfile {page:nonfile} PageName De tekenreeks mag geen punt bevatten in het laatste padsegment dat wordt gevolgd door één of meer niet-punt (.) tekens.

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Meerdere, door dubbele punt gescheiden beperkingen kunnen worden toegepast op één parameter. Met de volgende beperking wordt bijvoorbeeld een parameter beperkt tot een geheel getal van 1 of hoger:

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

Warning

Routebeperkingen die de URL controleren en worden geconverteerd naar een CLR-type, gebruiken altijd de invariante cultuur. Bijvoorbeeld conversie naar het CLR-type int of DateTime. Bij deze beperkingen wordt ervan uitgegaan dat de URL niet kan worden gelokaliseerd. De door het framework geleverde routebeperkingen wijzigen niet de waarden die zijn opgeslagen in routewaarden. Alle routewaarden die uit de URL worden geparseerd, worden opgeslagen als tekenreeksen. De beperking probeert bijvoorbeeld float de routewaarde te converteren naar een float, maar de geconverteerde waarde wordt alleen gebruikt om te controleren of deze kan worden geconverteerd naar een float.

Reguliere expressies in beperkingen

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Reguliere expressies kunnen worden opgegeven als inlinebeperkingen met behulp van de regex(...) routebeperking. Methoden in de MapControllerRoute familie accepteren ook een object-literal met beperkingen. Als dit formulier wordt gebruikt, worden tekenreekswaarden geïnterpreteerd als reguliere expressies.

De volgende code maakt gebruik van een inline regex-beperking:

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

In de volgende code wordt een letterlijk object gebruikt om een regex-beperking op te geven:

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

Het ASP.NET Core-framework voegt RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant toe aan de reguliere expressie-constructor. Zie RegexOptions voor een beschrijving van deze leden.

Reguliere expressies maken gebruik van scheidingstekens en tokens die vergelijkbaar zijn met tokens die worden gebruikt door routering en de C#-taal. Tokens voor reguliere expressies moeten worden geëscaped. Als u de reguliere expressie ^\d{3}-\d{2}-\d{4}$ in een inlinebeperking wilt gebruiken, gebruikt u een van de volgende opties:

  • Vervang \ tekens in de tekenreeks als \\ tekens in het C#-bronbestand om het escapeteken voor tekenreeksen \ te escapen.
  • Verbatim tekenreeksen.

Als u tekens voor scheidingstekens van routeringsparameters wilt escapen, verdubbel dan de tekens in de expressie, bijvoorbeeld {, }, [, ], {{, }}, [[, ]]. In de volgende tabel ziet u een reguliere expressie en de escape-versie:

Regular expression Geëscapede reguliere expressie
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Reguliere expressies die in routering worden gebruikt, beginnen vaak met het ^ teken en komen overeen met de beginpositie van de tekenreeks. De expressies eindigen vaak met het $ teken en komen overeen met het einde van de tekenreeks. De ^ en $ tekens zorgen ervoor dat de reguliere expressie overeenkomt met de volledige waarde van de routeparameter. Zonder de ^ en $ tekens komt de reguliere expressie overeen met een subtekenreeks binnen de tekenreeks, wat vaak ongewenst is. De volgende tabel bevat voorbeelden en legt uit waarom ze overeenkomen of niet overeenkomen:

Expression String Match Comment
[a-z]{2} hello Yes Substring matches
[a-z]{2} 123abc456 Yes Substring matches
[a-z]{2} mz Yes Matches expression
[a-z]{2} MZ Yes Niet hoofdlettergevoelig
^[a-z]{2}$ hello No Zie ^ en $ hierboven
^[a-z]{2}$ 123abc456 No Zie ^ en $ hierboven

Zie .NET Framework Regular Expressions voor meer informatie over de syntaxis van reguliere expressies.

Als u een parameter wilt beperken tot een bekende set mogelijke waarden, gebruikt u een reguliere expressie. Komt bijvoorbeeld {action:regex(^(list|get|create)$)} alleen overeen met de action routewaarde naar list, getof create. Wanneer dit wordt doorgegeven aan een woordenlijst met beperkingen, is de tekenreeks ^(list|get|create)$ gelijk. Beperkingen die worden doorgegeven in de woordenlijst met beperkingen die niet overeenkomen met een van de bekende beperkingen, worden ook beschouwd als reguliere expressies. Beperkingen die worden doorgegeven binnen een sjabloon die niet overeenkomen met een van de bekende beperkingen, worden niet behandeld als reguliere expressies.

Aangepaste routebeperkingen

Aangepaste routebeperkingen kunnen worden gemaakt door de IRouteConstraint interface te implementeren. De IRouteConstraint interface bevat Match, die retourneert true als aan de beperking wordt voldaan en false anders.

Aangepaste routebeperkingen zijn zelden nodig. Voordat u een aangepaste routebeperking implementeert, moet u alternatieven overwegen, zoals modelbinding.

De map ASP.NET Kernbeperkingen biedt goede voorbeelden van het maken van beperkingen. Bijvoorbeeld GuidRouteConstraint.

Als u een aangepast type IRouteConstraintroutebeperking wilt gebruiken, moet het type routebeperking worden geregistreerd bij de app ConstraintMap in de servicecontainer. Een ConstraintMap is een woordenlijst die routebeperkingssleutels toe wijst aan IRouteConstraint implementaties die deze beperkingen valideren. ConstraintMap van een app kan in Program.cs worden bijgewerkt als onderdeel van een AddRouting-oproep of door RouteOptions rechtstreeks met builder.Services.Configure<RouteOptions> te configureren. For example:

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

De voorgaande beperking wordt toegepast in de volgende code:

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

De implementatie van NoZeroesRouteConstraint voorkomt dat 0 in een routeparameter wordt gebruikt:

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

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

De voorgaande code:

  • Voorkomt 0 in het {id} segment van de route.
  • Wordt weergegeven om een eenvoudig voorbeeld te geven van het implementeren van een aangepaste beperking. Deze mag niet worden gebruikt in een productie-app.

De volgende code is een betere benadering om te voorkomen dat een id met een 0 wordt verwerkt.

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

    return Content(id);
}

De voorgaande code heeft de volgende voordelen ten opzichte van de NoZeroesRouteConstraint aanpak:

  • Er is geen aangepaste beperking vereist.
  • Het retourneert een meer beschrijvende fout wanneer de routeparameter 0 bevat.

Parameter transformers

Parameter transformers:

Een aangepaste slugify parametertransformator in routepatroon blog\{article:slugify} met Url.Action(new { article = "MyTestArticle" }) genereert blog\my-test-articlebijvoorbeeld .

Houd rekening met de volgende IOutboundParameterTransformer implementatie:

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

Als u een parametertransformatie in een routepatroon wilt gebruiken, configureert u deze met behulp van ConstraintMapProgram.cs:

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

Het ASP.NET Core-framework gebruikt parametertransformatoren om de URI te transformeren waarop een eindpunt wordt afgerond. Met parametertransformatoren worden bijvoorbeeld de routewaarden getransformeerd die worden gebruikt om overeen te komen met een area, controller, action, en page.

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

Met de voorgaande routesjabloon wordt de actie SubscriptionManagementController.GetAll vergeleken met de URI /subscription-management/get-all. Een parametertransformator wijzigt niet de routewaarden die worden gebruikt om een koppeling te genereren. Bijvoorbeeld Url.Action("GetAll", "SubscriptionManagement") produceert /subscription-management/get-all.

ASP.NET Core biedt API-conventies voor het gebruik van parametertransformatoren met gegenereerde routes:

Naslaginformatie over het genereren van URL's

Deze sectie bevat een verwijzing voor het algoritme dat is geïmplementeerd door het genereren van url's. In de praktijk gebruiken de meeste complexe voorbeelden van het genereren van URL's controllers of Razor pagina's. Zie routering in controllers voor meer informatie.

Het proces voor het genereren van URL's begint met een aanroep naar LinkGenerator.GetPathByAddress of een vergelijkbare methode. De methode wordt geleverd met een adres, een set routewaarden en eventueel informatie over de huidige aanvraag van HttpContext.

De eerste stap is het gebruik van het adres om een set kandidaat-eindpunten op te lossen met behulp van een IEndpointAddressScheme<TAddress> eindpunt dat overeenkomt met het type adres.

Zodra de set kandidaten door het adresschema is gevonden, worden de eindpunten iteratief gerangschikt en verwerkt totdat een URL-generatiebewerking is geslaagd. Het genereren van url's controleert niet op dubbelzinnigheden. Het eerste resultaat dat wordt geretourneerd, is het uiteindelijke resultaat.

Problemen bij het genereren van URL's met logboekregistratie oplossen

De eerste stap bij het oplossen van problemen met URL-generatie is het instellen van het niveau van de logboekregistratie op Microsoft.AspNetCore.RoutingTRACE. LinkGenerator registreert veel details over de verwerking die nuttig kan zijn om problemen op te lossen.

Zie naslaginformatie over het genereren van URL's voor meer informatie over het genereren van url's.

Addresses

Adressen zijn het concept in het genereren van URL's dat wordt gebruikt om een aanroep aan de koppelingsgenerator te binden aan een set kandidaat-eindpunten.

Adressen zijn een uitbreidbaar concept dat standaard wordt geleverd met twee implementaties:

  • Gebruik de eindpuntnaam (string) als het adres:
    • Biedt vergelijkbare functionaliteit als de routenaam van MVC.
    • Maakt gebruik van het IEndpointNameMetadata metagegevenstype.
    • Hiermee wordt de opgegeven reeks afgestemd op de metagegevens van alle geregistreerde eindpunten.
    • Genereert een uitzondering bij het opstarten als meerdere eindpunten dezelfde naam gebruiken.
    • Aanbevolen voor algemeen gebruik buiten controllers en Razor pagina's.
  • Routewaarden (RouteValuesAddress) gebruiken als het adres:
    • Biedt vergelijkbare functionaliteit voor controllers en Razor pagina's voor het genereren van verouderde URL's.
    • Zeer complex om uit te breiden en fouten op te sporen.
    • Biedt de implementatie die wordt gebruikt door IUrlHelper, Tag Helpers, HTML Helpers, Actieresultaten, enzovoort.

De rol van het adresschema is het maken van de koppeling tussen het adres en overeenkomende eindpunten op willekeurige criteria:

  • Het eindpuntnaamschema voert een eenvoudige woordenboekopzoeking uit.
  • Het schema voor routewaarden heeft een complex algoritme voor het bepalen van de beste subset van een set.

Omgevingswaarden en expliciete waarden

Vanuit de huidige aanvraag heeft routering toegang tot de routewaarden van de huidige aanvraag HttpContext.Request.RouteValues. De waarden die aan de huidige aanvraag zijn gekoppeld, worden de omgevingswaarden genoemd. Ter duidelijkheid verwijst de documentatie naar de routewaarden die worden doorgegeven aan methoden als expliciete waarden.

In het volgende voorbeeld ziet u omgevingswaarden en expliciete waarden. Het biedt omgevingswaarden van de huidige aanvraag en expliciete waarden:

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

    // ...

De voorgaande code:

De volgende code bevat alleen expliciete waarden en geen omgevingswaarden:

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

De voorgaande methode retourneert /Home/Subscribe/17

De volgende code in de WidgetController retourneert /Widget/Subscribe/17:

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

De volgende code biedt de controller vanuit omgevingsvariabelen in het huidige verzoek en expliciete waarden:

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

In de voorgaande code:

  • /Gadget/Edit/17 wordt geretourneerd.
  • Url haalt de IUrlHelper.
  • Action genereert een URL met een absoluut pad voor een actiemethode. De URL bevat de opgegeven action naam en route waarden.

De volgende code bevat omgevingswaarden van de huidige aanvraag en expliciete waarden:

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

        // ...
    }
}

De voorgaande code stelt url in op /Edit/17 wanneer de pagina Bewerken Razor de volgende pagina-instructie bevat:

@page "{id:int}"

Als de pagina Bewerken de "{id:int}" routesjabloon niet bevat, is url/Edit?id=17.

Het gedrag van MVC's IUrlHelper voegt een complexiteitslaag toe, naast de regels die hier worden beschreven:

  • IUrlHelper levert altijd de routewaarden van de huidige aanvraag als omgevingswaarden.
  • IUrlHelper.Action kopieert altijd de huidige action en controller routewaarden als expliciete waarden, tenzij deze worden overschreven door de ontwikkelaar.
  • IUrlHelper.Page kopieert de huidige page routewaarde altijd als een expliciete waarde, tenzij deze wordt overschreven.
  • IUrlHelper.Page overschrijft altijd de huidige handler routewaarde met null als expliciete waarde, tenzij null wordt overschreven.

Gebruikers worden vaak verrast door de gedragsdetails van omgevingswaarden, omdat MVC geen eigen regels lijkt te volgen. Om historische en compatibiliteitsredenen hebben bepaalde routewaarden zoals action, controller, page, en handler hun eigen speciale gedrag.

De equivalente functionaliteit die wordt geleverd door LinkGenerator.GetPathByAction en LinkGenerator.GetPathByPage dupliceert deze afwijkingen van IUrlHelper voor compatibiliteit.

Proces voor het genereren van URL's

Zodra de set kandidaat-eindpunten is gevonden, genereert het algoritme URL's.

  • Hiermee worden de eindpunten iteratief verwerkt.
  • Retourneert het eerste geslaagde resultaat.

De eerste stap in dit proces wordt routewaardevervalsing genoemd. Routewaarde-invalidering is het proces waarmee routering bepaalt welke routewaarden van de ambiente waarden moeten worden gebruikt en welke moeten worden genegeerd. Elke omgevingswaarde wordt beschouwd en gecombineerd met de expliciete waarden of genegeerd.

De beste manier om na te denken over de rol van omgevingswaarden is dat ze proberen toepassingsontwikkelaars te helpen minder te typen in veelvoorkomende gevallen. Normaal gesproken zijn de scenario's waarin omgevingswaarden nuttig zijn gerelateerd aan MVC:

  • Wanneer u een koppeling maakt naar een andere actie in dezelfde controller, hoeft de controllernaam niet te worden opgegeven.
  • Wanneer u een koppeling maakt naar een andere controller in hetzelfde gebied, hoeft de gebiedsnaam niet te worden opgegeven.
  • Bij het koppelen aan dezelfde actiemethode hoeven routewaarden niet te worden opgegeven.
  • Wanneer u een koppeling maakt naar een ander deel van de app, wilt u geen routewaarden overdragen die geen betekenis hebben in dat deel van de app.

Aanroepen naar LinkGenerator of IUrlHelper die terug null geven, worden meestal veroorzaakt door het niet begrijpen van de invalidering van de routewaarde. Los ongeldige routewaarden op door expliciet meer routewaarden op te geven om te zien of het probleem hierdoor wordt opgelost.

Invalidatie van routewaarden werkt volgens de veronderstelling dat het URL-schema van de app hiërarchisch is, met een hiërarchie die van links naar rechts is gevormd. Overweeg de basissjabloon {controller}/{action}/{id?} voor controllerroute om een intuïtief beeld te krijgen van hoe dit in de praktijk werkt. Als u een waarde wijzigt , worden alle routewaarden die rechts worden weergegeven, ongeldig . Dit weerspiegelt de aanname over hiërarchie. Als de app een omgevingswaarde heeft voor id, en de bewerking een andere waarde specificeert voor de controller:

  • id wordt niet hergebruikt omdat {controller} links van {id?} staat.

Enkele voorbeelden waarin dit principe wordt gedemonstreerd:

  • Als de expliciete waarden een waarde bevatten voor id, wordt de omgevingswaarde genegeerd id . De omgevingswaarden voor controller en action kunnen worden gebruikt.
  • Als de expliciete waarden een waarde bevatten voor action, wordt een omgevingswaarde action voor genegeerd. De omgevingswaarden voor controller kunnen worden gebruikt. Als de expliciete waarde action anders is dan de omgevingswaarde, actionwordt de id waarde niet gebruikt. Als de expliciete waarde voor action hetzelfde is als de omgevingswaarde voor action, kan de id waarde worden gebruikt.
  • Als de expliciete waarden een waarde bevatten voor controller, wordt een omgevingswaarde controller voor genegeerd. Als de expliciete waarde controller verschilt van de omgevingswaarde voor controller, worden de action waarden id niet gebruikt. Als de expliciete waarde voor controller dezelfde is als de omgevingswaarde voor controller, kunnen de waarden action en id worden gebruikt.

Dit proces wordt verder gecompliceerd door het bestaan van kenmerkroutes en toegewezen conventionele routes. Conventionele routes, zoals {controller}/{action}/{id?}, geven een hiërarchie aan met behulp van routeparameters. Voor specifieke conventionele routes en attributieroutes naar controllers en Razor pagina's:

  • Er is een hiërarchie met routewaarden.
  • Ze worden niet weergegeven in de sjabloon.

In deze gevallen definieert het genereren van URL's het concept van de vereiste waarden . Eindpunten die zijn gemaakt door controllers en Razor pagina's hebben vereiste waarden opgegeven waarmee routewaardevervalidering kan worden uitgevoerd.

Het invalidatie-algoritme voor de routewaarde in detail:

  • De vereiste waardenamen worden gecombineerd met de routeparameters en worden vervolgens van links naar rechts verwerkt.
  • Voor elke parameter worden de omgevingswaarde en expliciete waarde vergeleken:
    • Als de omgevingswaarde en expliciete waarde hetzelfde zijn, wordt het proces voortgezet.
    • Als de omgevingswaarde aanwezig is en de expliciete waarde niet is, wordt de omgevingswaarde gebruikt bij het genereren van de URL.
    • Als de omgevingswaarde niet aanwezig is en de expliciete waarde is, negeert u de omgevingswaarde en alle volgende omgevingswaarden.
    • Als de omgevingswaarde en de expliciete waarde aanwezig zijn en de twee waarden verschillen, negeert u de omgevingswaarde en alle volgende omgevingswaarden.

Op dit moment is de bewerking voor het genereren van URL's gereed om routebeperkingen te evalueren. De set van geaccepteerde waarden wordt samen met de standaardwaarden van de parameter verstrekt aan de beperkingen. Als aan alle beperkingen is voldaan, wordt de bewerking voortgezet.

Vervolgens kunnen de geaccepteerde waarden worden gebruikt om de routesjabloon uit te vouwen. De routesjabloon wordt verwerkt:

  • From left-to-right.
  • Elke parameter wordt vervangen door de geaccepteerde waarde.
  • Met de volgende speciale gevallen:
    • Als er een waarde ontbreekt in de geaccepteerde waarden en de parameter een standaardwaarde heeft, wordt de standaardwaarde gebruikt.
    • Als er een waarde ontbreekt in de geaccepteerde waarden en de parameter optioneel is, wordt de verwerking voortgezet.
    • Als een routeparameter rechts van een ontbrekende optionele parameter een waarde heeft, mislukt de bewerking.
    • Aaneengesloten parameters met standaardwaarden en optionele parameters worden indien mogelijk samengevouwen.

Waarden die expliciet zijn opgegeven die niet overeenkomen met een segment van de route, worden toegevoegd aan de querytekenreeks. In de volgende tabel ziet u het resultaat wanneer u de routesjabloon {controller}/{action}/{id?}gebruikt.

Ambient Values Explicit Values Result
controller = "Home" action = "Info" /Home/About
controller = "Home" controller = "Order", action = "About" /Order/About
controller = "Home", color = "Rood" action = "Info" /Home/About
controller = "Home" action = "About", color = "Red" /Home/About?color=Red

Optionele routeparametervolgorde

Optionele routeparameters moeten na alle vereiste routeparameters en letterlijke waarden komen te staan. In de volgende code moeten de id en name parameters na de color parameter worden geleverd:

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

Problemen met het ongeldig maken van routewaarde

De volgende code toont een voorbeeld van een URL-generatieschema dat niet wordt ondersteund door routering:

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

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

In de voorgaande code wordt de culture routeparameter gebruikt voor lokalisatie. De wens is om de culture parameter altijd als omgevingswaarde te laten accepteren. De culture parameter wordt echter niet geaccepteerd als een omgevingswaarde vanwege de manier waarop vereiste waarden werken:

  • In de "default" routesjabloon bevindt de culture routeparameter zich links van controller, zodat wijzigingen aan controllerculture niet ongeldig maken.
  • Binnen de "blog" routesjabloon wordt de culture routeparameter beschouwd als rechts van controller, die wordt weergegeven in de vereiste waarden.

URL-paden parseren met LinkParser

De LinkParser klasse voegt ondersteuning toe voor het parseren van een URL-pad in een set routewaarden. De ParsePathByEndpointName methode gebruikt een eindpuntnaam en een URL-pad en retourneert een set routewaarden die zijn geëxtraheerd uit het URL-pad.

In de volgende voorbeeldcontroller gebruikt de GetProduct actie een routesjabloon van api/Products/{id} en heeft een Name van GetProduct:

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

In dezelfde controllerklasse verwacht de AddRelatedProduct actie een URL-pad, pathToRelatedProductdat kan worden opgegeven als een queryreeksparameter:

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

    // ...

In het voorgaande voorbeeld haalt de AddRelatedProduct actie de id routewaarde op uit het URL-pad. Met een URL-pad van /api/Products/1bijvoorbeeld, wordt de relatedProductId waarde ingesteld op 1. Met deze benadering kunnen de CLIENTS van de API URL-paden gebruiken bij het verwijzen naar resources, zonder dat u hoeft te weten hoe een DERGELIJKE URL is gestructureerd.

Eindpuntmetagegevens configureren

De volgende koppelingen bevatten informatie over het configureren van eindpuntmetagegevens:

Hostkoppeling in routes met RequireHost

RequireHost past een beperking toe op de route waarvoor de opgegeven host is vereist. De RequireHost-parameter of de [Host] kan een:

  • Host: www.domain.com, komt www.domain.com overeen met elke poort.
  • Host met wildcard: *.domain.com, komt overeen met www.domain.com, subdomain.domain.com, of www.subdomain.domain.com op een willekeurige poort.
  • Poort: *:5000, komt overeen met poort 5000 met elke host.
  • Host en poort: www.domain.com:5000 of *.domain.com:5000, komt overeen met host en poort.

Er kunnen meerdere parameters worden opgegeven met of RequireHost[Host]. De beperking komt overeen met hosts die geldig zijn voor een van de parameters. Bijvoorbeeld, [Host("domain.com", "*.domain.com")] komt overeen met domain.com, www.domain.com, en subdomain.domain.com.

De volgende code gebruikt RequireHost om de opgegeven host op de route te vereisen:

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

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

De volgende code gebruikt het [Host] kenmerk op de controller om een van de opgegeven hosts te vereisen:

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

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

Wanneer het [Host] kenmerk wordt toegepast op zowel de controller als de actiemethode:

  • Het kenmerk van de actie wordt gebruikt.
  • Het controllerkenmerk wordt genegeerd.

Warning

API die afhankelijk is van de hostheader, zoals HttpRequest.Host en RequireHost, is onderhevig aan mogelijke adresvervalsing door clients.

Als u host- en poortvervalsing wilt voorkomen, gebruikt u een van de volgende methoden:

Route groups

Met de MapGroup-extensiemethode kunt u groepen eindpunten organiseren met een gemeenschappelijk voorvoegsel. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata waarmee metagegevens van eindpuntenworden toegevoegd.

Met de volgende code worden bijvoorbeeld twee vergelijkbare groepen eindpunten gemaakt:

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

In dit scenario kunt u een relatief adres gebruiken voor de Location-header in het 201 Created resultaat:

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

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

De eerste groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /public/todos en zijn toegankelijk zonder verificatie. De tweede groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /private/todos en vereisen verificatie.

De QueryPrivateTodoseindpuntfilterfactory is een lokale functie waarmee de TodoDb parameters van de route-handler worden gewijzigd om toegang te krijgen tot privétodogegevens en deze op te slaan.

Routegroepen ondersteunen ook geneste groepen en complexe voorvoegselpatronen met routeparameters en beperkingen. In het volgende voorbeeld kan de routehandler die is toegewezen aan de user groep, de {org} en {group} routeparameters vastleggen die zijn gedefinieerd in de voorvoegsels van de buitenste groep.

Het voorvoegsel kan ook leeg zijn. Dit kan handig zijn voor het toevoegen van eindpuntmetagegevens of filters aan een groep eindpunten zonder het routepatroon te wijzigen.

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

Het toevoegen van filters of metagegevens aan een groep gedraagt zich op dezelfde manier als het afzonderlijk toevoegen van filters of metagegevens aan elk eindpunt voordat u extra filters of metagegevens toevoegt die mogelijk zijn toegevoegd aan een binnenste groep of specifiek eindpunt.

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

In het bovenstaande voorbeeld registreert het buitenste filter de binnenkomende aanvraag voordat het binnenste filter de kans krijgt om dat te doen, hoewel het binnenste filter als tweede is toegevoegd. Omdat de filters zijn toegepast op verschillende groepen, maakt de volgorde die ze ten opzichte van elkaar zijn toegevoegd niet uit. De volgorde waarin filters worden toegevoegd, maakt uit of ze op dezelfde groep of een specifiek eindpunt worden toegepast.

Een aanvraag voor /outer/inner/ meldt het volgende:

/outer group filter
/inner group filter
MapGet filter

Richtlijnen voor prestaties voor routering

Wanneer een app prestatieproblemen heeft, wordt routering vaak vermoed als het probleem. De reden waarom routering wordt vermoed, is dat frameworks zoals controllers en Razor Pagina's de hoeveelheid tijd rapporteren die binnen het framework is besteed in hun logboekberichten. Wanneer er een aanzienlijk verschil is tussen de tijd die door controllers wordt gerapporteerd en de totale tijd van de aanvraag:

  • Ontwikkelaars elimineren hun app-code als de bron van het probleem.
  • Het is gebruikelijk om aan te nemen dat routering de oorzaak is.

Routering wordt getest met behulp van duizenden eindpunten. Het is onwaarschijnlijk dat een typische app een prestatieprobleem ondervindt door te groot te zijn. De meest voorkomende hoofdoorzaak van trage routeringsprestaties is meestal een slecht gedragende aangepaste middleware.

In dit volgende codevoorbeeld ziet u een basistechniek voor het beperken van de bron van vertraging:

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

Voor tijdsplanning van routering:

  • Interleave elke middleware met een kopie van de timing-middleware die in de code hierboven wordt weergegeven.
  • Voeg een unieke id toe om de timinggegevens te correleren met de code.

Dit is een eenvoudige manier om de vertraging te beperken wanneer deze aanzienlijk is, bijvoorbeeld meer dan 10ms. Het aftrekken van Time 2 van Time 1 geeft de tijd weer die binnen de UseRouting middleware is besteed.

De volgende code maakt gebruik van een compactere benadering van de voorgaande tijdscode:

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

Mogelijk dure routeringsfuncties

De volgende lijst biedt inzicht in routeringsfuncties die relatief duur zijn vergeleken met basisroutesjablonen:

  • Reguliere expressies: het is mogelijk om reguliere expressies te schrijven die complex zijn of langlopende tijd hebben met een kleine hoeveelheid invoer.
  • Complexe segmenten ({x}-{y}-{z}):
    • Zijn aanzienlijk duurder dan het parseren van een normaal URL-padsegment.
    • Resulteert in veel meer subtekenreeksen die toegewezen worden.
  • Synchrone gegevenstoegang: veel complexe apps hebben databasetoegang als onderdeel van hun routering. Gebruik uitbreidbaarheidspunten zoals MatcherPolicy en EndpointSelectorContext, die asynchroon zijn.

Richtlijnen voor grote routetabellen

Standaard gebruikt ASP.NET Core een routeringsalgoritmen die geheugen ruilt voor CPU-tijd. Dit heeft het mooie effect dat routekoppelingstijd alleen afhankelijk is van de lengte van het pad dat moet worden vergeleken en niet het aantal routes. Deze benadering kan echter in sommige gevallen problematisch zijn, wanneer de app een groot aantal routes (in duizenden) heeft en er een grote hoeveelheid variabele voorvoegsels in de routes is. Als de routes bijvoorbeeld parameters hebben in vroege segmenten van de route, zoals {parameter}/some/literal.

Het is onwaarschijnlijk dat een app een situatie ondervindt waarbij dit een probleem is, tenzij:

  • Er is een groot aantal routes in de app met behulp van dit patroon.
  • Er is een groot aantal routes in de app.

Bepalen of een app het probleem met de grote routetabel ondervindt

  • Er zijn twee symptomen om naar te zoeken:
    • De app wordt langzaam gestart bij de eerste aanvraag.
      • Houd er rekening mee dat dit vereist is, maar niet voldoende. Er zijn veel andere niet-routeproblemen die kunnen leiden tot tragere opstart van apps. Controleer de onderstaande voorwaarde om nauwkeurig te bepalen of de app in deze situatie terechtkomt.
    • De app verbruikt veel geheugen tijdens het opstarten en een geheugendump toont een groot aantal Microsoft.AspNetCore.Routing.Matching.DfaNode exemplaren.

Dit probleem oplossen

Er zijn verschillende technieken en optimalisaties die kunnen worden toegepast op routes die dit scenario grotendeels verbeteren:

  • Pas waar mogelijk routebeperkingen toe op uw parameters, bijvoorbeeld {parameter:int}{parameter:guid}, {parameter:regex(\\d+)}enzovoort.
    • Hierdoor kan het routeringsalgoritme intern de structuren optimaliseren die worden gebruikt voor het vergelijken en drastisch verminderen van het gebruikte geheugen.
    • In de overgrote meerderheid van de gevallen volstaat dit om terug te keren naar een aanvaardbaar gedrag.
  • Wijzig de routes om parameters naar latere segmenten in de sjabloon te verplaatsen.
    • Het vermindert het aantal mogelijke paden dat overeenkomt met een eindpunt, gegeven een bepaald pad.
  • Gebruik een dynamische route en voer de koppeling dynamisch uit naar een controller/pagina.
    • Dit kan worden bereikt met behulp van MapDynamicControllerRoute en MapDynamicPageRoute.

Middleware voor kortsluiten na de routering

Wanneer routering overeenkomt met een eindpunt, laat het doorgaans de rest van de middleware-pijplijn uitvoeren voordat de eindpuntlogica wordt aangeroepen. Services kunnen het resourcegebruik verminderen door bekende aanvragen vroeg in de pijplijn te filteren. Gebruik de ShortCircuit-extensiemethode om te zorgen dat routering de eindpuntlogica onmiddellijk aanroept en vervolgens de aanvraag beëindigt. Een bepaalde route hoeft bijvoorbeeld mogelijk geen verificatie of CORS-middleware te doorlopen. In het volgende voorbeeld worden verzoeken die overeenkomen met de /short-circuit route kortgesloten.

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

De ShortCircuit(IEndpointConventionBuilder, Nullable<Int32>) methode kan eventueel een statuscode gebruiken.

Gebruik de methode MapShortCircuit om kortsluiting voor meerdere routes tegelijk in te stellen door een parametermatrix met URL-voorvoegsels door te geven. Browsers en bots testen bijvoorbeeld vaak servers voor bekende paden zoals robots.txt en favicon.ico. Als de app deze bestanden niet heeft, kan één regel code beide routes configureren:

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

MapShortCircuit retourneert IEndpointConventionBuilder zodat er extra routebeperkingen, zoals hostfilters, aan kunnen worden toegevoegd.

De ShortCircuit en MapShortCircuit methoden zijn niet van invloed op middleware die eerder UseRoutingzijn geplaatst. Als u deze methoden probeert te gebruiken met eindpunten die zowel over [Authorize] als [RequireCors] metagegevens beschikken, zullen aanvragen mislukken met een InvalidOperationException. Deze metagegevens worden toegepast op [Authorize] of [EnableCors] kenmerken of RequireCorsRequireAuthorization methoden.

Als u het effect van kortsluitende middleware wilt zien, stelt u de categorie "Microsoft" logging in op "Informatie" in appsettings.Development.json:

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

Voer de volgende code uit:

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

Het volgende voorbeeld is afkomstig uit de consolelogs die worden geproduceerd door het endpoint / uit te voeren. Het bevat uitvoer van de middleware voor logboekregistratie:

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

In het volgende voorbeeld wordt het /short-circuit eindpunt uitgevoerd. Het heeft niets van de middleware voor logboekregistratie omdat de middleware kortsluiting heeft:

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.

Richtlijnen voor bibliotheekauteurs

Deze sectie bevat richtlijnen voor auteurs van bibliotheken die voortbouwen op routering. Deze details zijn bedoeld om ervoor te zorgen dat app-ontwikkelaars een goede ervaring hebben met het gebruik van bibliotheken en frameworks waarmee routering wordt uitgebreid.

Define endpoints

Als u een framework wilt maken dat gebruikmaakt van routing voor URL-koppeling, begint u met het definiëren van een gebruikerservaring die voortbouwt op UseEndpoints.

DO bovenop IEndpointRouteBuilder bouwen. Hierdoor kunnen gebruikers uw framework opstellen met andere ASP.NET Core-functies zonder verwarring. Elke ASP.NET Core-sjabloon bevat routering. Stel dat routering aanwezig is en bekend is voor gebruikers.

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

app.MapHealthChecks("/healthz");

DO retourneert een verzegeld betontype van een aanroep naar MapMyFramework(...) dat implementeert IEndpointConventionBuilder. De meeste frameworkmethoden Map... volgen dit patroon. De IEndpointConventionBuilder interface:

  • Hiermee kunnen metagegevens worden samengesteld.
  • Is gericht op verschillende uitbreidingsmethoden.

Door uw eigen type te declareren, kunt u uw eigen frameworkspecifieke functionaliteit toevoegen aan de opbouwfunctie. Het is prima om een door het framework gedeclareerde bouwer in te pakken en oproepen door te sturen.

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

app.MapHealthChecks("/healthz");

OVERWEEG om je eigen EndpointDataSourcete schrijven. EndpointDataSource is het primitieve laag-niveau mechanisme voor het declareren en bijwerken van een verzameling eindpunten. EndpointDataSource is een krachtige API die wordt gebruikt door controllers en Razor Pages. Zie Dynamische eindpuntroutering voor meer informatie.

De routeringstests hebben een eenvoudig voorbeeld van een gegevensbron die niet wordt bijgewerkt.

OVERWEEG om te implementeren GetGroupedEndpoints. Dit geeft volledige controle over het uitvoeren van groepsconventies en de uiteindelijke metagegevens op de gegroepeerde eindpunten. Hierdoor kunnen aangepaste EndpointDataSource implementaties bijvoorbeeld eindpuntfilters uitvoeren die zijn toegevoegd aan groepen.

PROBEER NIET standaard een EndpointDataSource registratie uit te voeren. Vereisen dat gebruikers uw framework registreren in UseEndpoints. De filosofie van routering is dat er standaard niets is inbegrepen en dat UseEndpoints is de plek om eindpunten te registreren.

Routerings-geïntegreerde middleware maken

OVERWEEG metagegevenstypen te definiëren als een interface.

DO maakt het mogelijk om metagegevenstypen te gebruiken als een kenmerk voor klassen en methoden.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

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

Frameworks zoals controllers en Razor pagina's ondersteunen het toepassen van metagegevenskenmerken op typen en methoden. Als u metagegevenstypen declareert:

  • Maak ze toegankelijk als kenmerken.
  • De meeste gebruikers zijn bekend met het toepassen van kenmerken.

Door een metagegevenstype als interface te declareren, voegt u een andere flexibiliteitslaag toe:

  • Interfaces zijn samenstelbaar.
  • Ontwikkelaars kunnen hun eigen typen declareren die meerdere beleidsregels combineren.

DO maakt het mogelijk om metagegevens te overschrijven, zoals wordt weergegeven in het volgende voorbeeld:

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

De beste manier om deze richtlijnen te volgen, is om te voorkomen dat metagegevens van markeringen worden gedefinieerd:

  • Zoek niet alleen naar de aanwezigheid van een metagegevenstype.
  • Definieer een eigenschap op de metagegevens en controleer de eigenschap.

De verzameling metagegevens wordt gerangschikt en ondersteunt het overschrijven op prioriteit. In het geval van controllers zijn metagegevens voor de actiemethode het meest specifiek.

Maak middleware nuttig met en zonder routering:

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

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

Bekijk als voorbeeld van deze richtlijn de UseAuthorization middleware. Met de middleware voor autorisatie kunt u een terugvalbeleid doorgeven. Het terugvalbeleid, indien opgegeven, is van toepassing op beide:

  • Eindpunten zonder een opgegeven beleid.
  • Aanvragen die niet overeenkomen met een eindpunt.

Dit maakt de autorisatie-middleware nuttig buiten de context van routering. De autorisatie-middleware kan worden gebruikt voor traditionele middlewareprogrammering.

Debug diagnostics

Voor gedetailleerde routeringsdiagnose-uitvoer, stel Logging:LogLevel:Microsoft in op Debug. Stel in de ontwikkelomgeving het logboekniveau in:appsettings.Development.json

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

Additional resources

Routering is verantwoordelijk voor het koppelen van binnenkomende HTTP-aanvragen en het verzenden van deze aanvragen naar de uitvoerbare eindpunten van de app. Eindpunten zijn de eenheden van uitvoerbare aanvraagafhandelingscode van de app. Eindpunten worden gedefinieerd in de app en geconfigureerd wanneer de app wordt gestart. Het overeenkomende eindpuntproces kan waarden extraheren uit de URL van de aanvraag en deze waarden opgeven voor het verwerken van aanvragen. Met behulp van eindpuntgegevens van de app kan routering ook URL's genereren die zijn toegewezen aan eindpunten.

Apps kunnen routering configureren met behulp van:

In dit artikel worden details van ASP.NET Core-routering op laag niveau beschreven. Voor informatie over het configureren van routering:

Routing basics

De volgende code toont een basisvoorbeeld van routering:

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

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

app.Run();

Het voorgaande voorbeeld bevat één eindpunt met behulp van de MapGet methode:

  • Wanneer een HTTP-aanvraag GET naar de hoofd-URL /wordt verzonden:
    • De gemachtigde van de aanvraag wordt uitgevoerd.
    • Hello World! wordt naar het HTTP-antwoord geschreven.
  • Als de aanvraagmethode niet GET is of de hoofd-URL niet /is, worden er geen routeovereenkomsten en wordt een HTTP 404 geretourneerd.

Routering maakt gebruik van een paar middleware, geregistreerd door UseRouting en UseEndpoints:

  • UseRouting voegt routekoppeling toe aan de middleware-pijplijn. Deze middleware bekijkt de set eindpunten die in de app zijn gedefinieerd en selecteert de beste overeenkomst op basis van de aanvraag.
  • UseEndpoints voegt eindpuntuitvoering toe aan de middleware-pijplijn. Het voert de delegate uit die is gekoppeld aan het geselecteerde eindpunt.

Apps hoeven meestal niet UseRouting of UseEndpoints aan te roepen. WebApplicationBuilder configureert een middleware-pijplijn die middleware die is toegevoegd in Program.cs omhult met UseRouting en UseEndpoints. Apps kunnen echter de volgorde wijzigen waarin UseRouting en UseEndpoints worden uitgevoerd door deze methoden expliciet aan te roepen. Met de volgende code wordt bijvoorbeeld een expliciete aanroep uitgevoerd naar UseRouting:

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

app.UseRouting();

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

In de voorgaande code:

  • Het aanroepen van app.Use registreert een aangepaste middleware die aan het begin van de pijplijn wordt uitgevoerd.
  • De aanroep voor UseRouting het configureren van de route die overeenkomt met middleware die moet worden uitgevoerd na de aangepaste middleware.
  • Het eindpunt dat is geregistreerd bij MapGet wordt aan het einde van de pijplijn uitgevoerd.

Als er in het voorgaande voorbeeld geen aanroep van UseRouting was opgenomen, zou de aangepaste middleware worden uitgevoerd na de route-matchende middleware.

Endpoints

De MapGet methode wordt gebruikt om een eindpunt te definiëren. Een eindpunt is iets dat het volgende kan zijn:

  • Geselecteerd door de URL en HTTP-methode te koppelen.
  • Uitgevoerd door de gemachtigde uit te voeren.

Eindpunten die door de app kunnen worden vergeleken en uitgevoerd, worden geconfigureerd in UseEndpoints. MapGet MapPost Vergelijkbare methoden verbinden aanvraagafgevaardigden met het routeringssysteem. Aanvullende methoden kunnen worden gebruikt om ASP.NET Core-frameworkfuncties te verbinden met het routeringssysteem:

In het volgende voorbeeld ziet u routering met een geavanceerdere routesjabloon:

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

De tekenreeks /hello/{name:alpha} is een routesjabloon. Er wordt een routesjabloon gebruikt om te configureren hoe het eindpunt wordt gekoppeld. In dit geval komt de sjabloon overeen met:

  • Een URL zoals /hello/Docs
  • Elk URL-pad dat begint met /hello/ gevolgd door een reeks alfabetische tekens. :alpha past een routebeperking toe die alleen overeenkomt met alfabetische tekens. Routebeperkingen worden verderop in dit artikel uitgelegd.

Het tweede segment van het URL-pad: {name:alpha}

In het volgende voorbeeld ziet u routering met statuscontroles en autorisatie:

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

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

In het voorgaande voorbeeld ziet u hoe:

  • De middleware voor autorisatie kan worden gebruikt met routering.
  • Eindpunten kunnen worden gebruikt om autorisatiegedrag te configureren.

De MapHealthChecks aanroep voegt een statuscontrole-eindpunt toe. Door RequireAuthorization aan deze oproep toe te voegen, wordt een autorisatiebeleid aan het eindpunt toegevoegd.

Door UseAuthentication en UseAuthorization aan te roepen, wordt de verificatie- en autorisatie-middleware toegevoegd. Deze middleware worden geplaatst tussen UseRouting en UseEndpoints zodat ze het volgende kunnen:

  • Zien welk eindpunt is geselecteerd door UseRouting.
  • Pas een autorisatiebeleid toe voordat UseEndpoints naar het eindpunt wordt verzonden.

Endpoint metadata

In het voorgaande voorbeeld zijn er twee eindpunten, maar alleen het eindpunt voor de statuscontrole heeft een autorisatiebeleid gekoppeld. Als de aanvraag overeenkomt met het eindpunt van de statuscontrole, /healthzwordt er een autorisatiecontrole uitgevoerd. Dit laat zien dat er extra gegevens aan eindpunten kunnen worden gekoppeld. Deze extra gegevens worden eindpuntmetagegevens genoemd:

  • De metagegevens kunnen worden verwerkt door middel van routeringsbewuste middleware.
  • De metagegevens kunnen van elk .NET-type zijn.

Routing concepts

Het routeringssysteem bouwt voort op de middleware-pijplijn door het krachtige eindpuntconcept toe te voegen. Eindpunten vertegenwoordigen eenheden van de functionaliteit van de app die van elkaar verschillen wat betreft routering, autorisatie en een willekeurig aantal ASP.NET Core-systemen.

ASP.NET Kerneindpuntdefinitie

Een ASP.NET Core-eindpunt is:

De volgende code laat zien hoe u het eindpunt ophaalt en inspecteert dat overeenkomt met de huidige aanvraag:

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

Het eindpunt, indien geselecteerd, kan worden opgehaald uit de HttpContext. De eigenschappen kunnen worden geïnspecteerd. Eindpuntobjecten zijn onveranderbaar en kunnen niet worden gewijzigd na het maken. Het meest voorkomende type eindpunt is een RouteEndpoint. RouteEndpoint bevat informatie waarmee deze kan worden geselecteerd door het routeringssysteem.

In de voorgaande code configureert app.Use een inline-middleware.

In de volgende code blijkt dat er, afhankelijk van waar in de pijplijn app.Use wordt aangeroepen, mogelijk geen eindpunt is.

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

In het voorgaande voorbeeld worden instructies toegevoegd Console.WriteLine die aangeven of er al dan niet een eindpunt is geselecteerd. Ter duidelijkheid wijst het voorbeeld een weergavenaam toe aan het opgegeven / eindpunt.

Het voorgaande voorbeeld bevat ook aanroepen naar UseRouting en UseEndpoints om precies te bepalen wanneer deze middleware in de pijplijn wordt uitgevoerd.

Als u deze code uitvoert met een URL van /, wordt:

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

Als u deze code uitvoert met een andere URL, wordt het volgende weergegeven:

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

Deze uitvoer laat zien dat:

  • Het eindpunt is altijd null voordat UseRouting wordt aangeroepen.
  • Als er een overeenkomst wordt gevonden, is het eindpunt niet null tussen UseRouting en UseEndpoints.
  • De UseEndpoints middleware is terminal wanneer er een overeenkomst wordt gevonden. Terminal middleware wordt verderop in dit artikel gedefinieerd.
  • Na UseEndpoints wordt de middleware alleen uitgevoerd als er geen overeenkomst wordt gevonden.

De UseRouting middleware gebruikt de SetEndpoint methode om het eindpunt aan de huidige context te koppelen. Het is mogelijk om de UseRouting middleware te vervangen door aangepaste logica en nog steeds de voordelen te krijgen van het gebruik van eindpunten. Eindpunten zijn een laag-niveau primitief zoals middleware en zijn niet gekoppeld aan de routeringsimplementatie. De meeste apps hoeven UseRouting niet te vervangen door aangepaste logica.

De UseEndpoints middleware is ontworpen om te worden gebruikt in combinatie met de UseRouting middleware. De kernlogica voor het uitvoeren van een eindpunt is niet ingewikkeld. Gebruik GetEndpoint om het eindpunt op te halen en roep vervolgens de eigenschap van RequestDelegate aan.

De volgende code laat zien hoe middleware invloed kan hebben op of kan reageren op routering:

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

In het voorgaande voorbeeld ziet u twee belangrijke concepten:

  • Middleware kan worden uitgevoerd voordat UseRouting om de gegevens waarop routering plaatsvindt te wijzigen.
  • Middleware kan worden uitgevoerd tussen UseRouting en UseEndpoints om de resultaten van routering te verwerken voordat het eindpunt wordt uitgevoerd.
    • Middleware die wordt uitgevoerd tussen UseRouting en UseEndpoints:
      • Inspecteert meestal metagegevens om inzicht te hebben in de eindpunten.
      • Vaak neemt u beveiligingsbeslissingen, zoals gedaan door UseAuthorization en UseCors.
    • Met de combinatie van middleware en metagegevens kunt u beleidsregels per eindpunt configureren.

In de voorgaande code ziet u een voorbeeld van een aangepaste middleware die beleid per eindpunt ondersteunt. De middleware schrijft een auditlog van de toegang tot gevoelige gegevens naar de console. De middleware kan worden geconfigureerd om een eindpunt te controleren met de RequiresAuditAttribute metagegevens. In dit voorbeeld ziet u een opt-in-patroon waarbij alleen eindpunten die als gevoelig zijn gemarkeerd, worden gecontroleerd. Het is mogelijk om deze logica omgekeerd te definiëren en alles te controleren dat niet als veilig is gemarkeerd, bijvoorbeeld. Het eindpuntmetagegevenssysteem is flexibel. Deze logica kan op welke manier dan ook worden ontworpen voor de use-case.

De voorgaande voorbeeldcode is bedoeld om de basisconcepten van eindpunten te demonstreren. Het voorbeeld is niet bedoeld voor productiegebruik. Een volledigere versie van een middleware voor auditlogboeken zou het volgende doen:

  • Meld u aan bij een bestand of database.
  • Neem details op, zoals de gebruiker, het IP-adres, de naam van het gevoelige eindpunt en meer.

De metagegevens RequiresAuditAttribute van het controlebeleid worden gedefinieerd als een Attribute voor eenvoudiger gebruik met frameworks op basis van klassen, zoals controllers en SignalR. Wanneer u route naar code gebruikt:

  • Metagegevens worden gekoppeld aan een builder-API.
  • Frameworks op basis van klassen bevatten alle kenmerken van de bijbehorende methode en klasse bij het maken van eindpunten.

De aanbevolen procedures voor metagegevenstypen zijn om ze te definiëren als interfaces of kenmerken. Interfaces en kenmerken staan hergebruik van code toe. Het metagegevenssysteem is flexibel en legt geen beperkingen op.

Terminal-middleware vergelijken met routering

In het volgende voorbeeld ziet u zowel terminal-middleware als routering:

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

De stijl van middleware die wordt weergegeven met Approach 1: is terminal-middleware. Dit wordt terminal-middleware genoemd omdat er een overeenkomende bewerking wordt uitgevoerd:

  • De overeenkomende bewerking in het voorgaande voorbeeld is Path == "/" voor de middleware en Path == "/Routing" voor routering.
  • Wanneer een overeenkomst succesvol is, wordt bepaalde functionaliteit uitgevoerd en wordt er teruggekeerd zonder dat de next middleware wordt aangeroepen.

Dit wordt terminal middleware genoemd omdat de zoekopdracht wordt beëindigd, bepaalde functionaliteit wordt uitgevoerd en vervolgens wordt geretourneerd.

De volgende lijst vergelijkt terminal-middleware met routering:

  • Beide benaderingen maken het beëindigen van de verwerkingspijplijn mogelijk:
    • Middleware beëindigt de pijplijn door te retourneren in plaats van aan te nextroepen.
    • Eindpunten zijn altijd terminal.
  • Met terminal-middleware kunt u de middleware op een willekeurige plaats in de pijplijn plaatsen:
    • Eindpunten worden uitgevoerd op de positie van UseEndpoints.
  • Met terminal-middleware kan willekeurige code worden bepaald wanneer de middleware overeenkomt:
    • Aangepaste routingcode kan langdradig zijn en moeilijk correct te schrijven.
    • Routering biedt eenvoudige oplossingen voor typische apps. Voor de meeste apps is geen aangepaste routekoppelingscode vereist.
  • Eindpuntinterface met middleware zoals UseAuthorization en UseCors.
    • Het gebruik van een terminal-middleware met UseAuthorization of UseCors vereist handmatige interfacing met het autorisatiesysteem.

Een eindpunt definieert beide:

  • Een gemachtigde voor het verwerken van aanvragen.
  • Een verzameling willekeurige metagegevens. De metagegevens worden gebruikt om kruislingse problemen te implementeren op basis van beleidsregels en configuratie die aan elk eindpunt zijn gekoppeld.

Terminal-middleware kan een effectief hulpprogramma zijn, maar kan het volgende vereisen:

  • Een aanzienlijke hoeveelheid coderen en testen.
  • Handmatige integratie met andere systemen om het gewenste flexibiliteitsniveau te bereiken.

Overweeg om te integreren met routering voordat u een terminal-middleware schrijft.

Bestaande terminal-middleware die kan worden geïntegreerd met Kaart of MapWhen kan meestal worden omgezet in een routeringsbewust eindpunt. MapHealthChecks demonstreert het patroon voor router-ware:

  • Schrijf een extensiemethode op IEndpointRouteBuilder.
  • Maak een geneste middleware-pijplijn met behulp van CreateApplicationBuilder.
  • Koppel de middleware aan de nieuwe pijplijn. In dit geval UseHealthChecks.
  • Build de middleware-pijplijn naar een RequestDelegate.
  • Roep Map aan en geef de nieuwe middleware-pijplijn op.
  • Het opbouwobject dat door de extensiemethode Map wordt geleverd, wordt geretourneerd.

De volgende code toont het gebruik van MapHealthChecks:

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

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

In het voorgaande voorbeeld ziet u waarom het retourneren van het opbouwobject belangrijk is. Als het opbouwobject wordt geretourneerd, kan de app-ontwikkelaar beleidsregels configureren, zoals autorisatie voor het eindpunt. In dit voorbeeld heeft de gezondheidscontroles middleware geen directe integratie met het autorisatiesysteem.

Het metagegevenssysteem is gemaakt als reactie op de problemen die zijn opgetreden door auteurs van uitbreidbaarheid met behulp van terminal-middleware. Het is problematisch voor elke middleware om een eigen integratie met het autorisatiesysteem te implementeren.

URL matching

  • Is het proces waarmee routering overeenkomt met een binnenkomende aanvraag naar een eindpunt.
  • Is gebaseerd op gegevens in het URL-pad en de headers.
  • Kan worden uitgebreid om rekening te houden met alle gegevens in de aanvraag.

Wanneer een routing-middleware wordt uitgevoerd, worden een Endpoint en routewaarden ingesteld op een verzoekfunctie van het HttpContext huidige verzoek:

Middleware wordt uitgevoerd nadat de routerings-middleware het eindpunt kan inspecteren en actie kan ondernemen. Een autorisatie-middleware kan bijvoorbeeld de metagegevensverzameling van het eindpunt ondervragen voor een autorisatiebeleid. Nadat alle middleware in de pijplijn voor aanvraagverwerking is uitgevoerd, wordt de gemachtigde van het geselecteerde eindpunt aangeroepen.

Het routeringssysteem in eindpuntroutering is verantwoordelijk voor alle verzendbeslissingen. Omdat de middleware beleidsregels toepast op basis van het geselecteerde eindpunt, is het belangrijk dat:

  • Elke beslissing die van invloed kan zijn op verzending of de toepassing van beveiligingsbeleid wordt gemaakt in het routeringssysteem.

Warning

Voor achterwaartse compatibiliteit, wanneer een controller- of Razor pagina's eindpuntdelegatie wordt uitgevoerd, worden de eigenschappen van RouteContext.RouteData ingesteld op de juiste waarden op basis van de verwerking van de aanvraag tot nu toe.

Het RouteContext type wordt gemarkeerd als verouderd in een toekomstige release:

  • Migreren RouteData.Values naar HttpRequest.RouteValues.
  • Migreer RouteData.DataTokens om IDataTokensMetadata uit de eindpuntmetagegevens op te halen.

URL-vergelijking werkt in een configureerbare set fasen. In elke fase is de uitvoer een verzameling van overeenkomsten. De reeks wedstrijden kan verder worden beperkt door de volgende fase. De implementatie van routering garandeert geen verwerkingsvolgorde voor overeenkomende eindpunten. Alle mogelijke overeenkomsten worden in één keer verwerkt. De fasen van het overeenkomen van de URL vinden plaats in de volgende volgorde. ASP.NET Core:

  1. Verwerkt het URL-pad op basis van de set eindpunten en de bijbehorende routesjablonen, en verzamelt alle overeenkomsten.
  2. Neemt de voorgaande lijst, en verwijdert overeenkomsten die niet voldoen aan de toegepaste routebeperkingen.
  3. Neemt de voorgaande lijst en verwijdert overeenkomsten die falen in de set MatcherPolicy instanties.
  4. EndpointSelector Hiermee kunt u een definitieve beslissing nemen uit de voorgaande lijst.

De lijst met eindpunten wordt gerangschikt op basis van:

Alle overeenkomende eindpunten worden in elke fase verwerkt totdat het EndpointSelector is bereikt. Het EndpointSelector is de laatste fase. Het systeem kiest het eindpunt met de hoogste prioriteit van de overeenkomsten als beste overeenkomst. Als er andere overeenkomsten zijn met dezelfde prioriteit als de beste overeenkomst, wordt er een dubbelzinnige match-exceptie opgeworpen.

De routeprioriteit wordt berekend op basis van een specifiekere routesjabloon die een hogere prioriteit krijgt. Denk bijvoorbeeld aan de sjablonen /hello en /{message}:

  • Beide komen overeen met het URL-pad /hello.
  • /hello is specifieker en dus hogere prioriteit.

Over het algemeen doet routeprioriteit goed werk bij het kiezen van de beste match voor de soorten URL-schema's die in de praktijk worden gebruikt. Gebruik Order deze functie alleen wanneer dat nodig is om dubbelzinnigheid te voorkomen.

Vanwege de soorten uitbreidbaarheid die wordt geboden door routering, is het niet mogelijk dat het routeringssysteem van tevoren de ambigu routes berekent. Bekijk een voorbeeld zoals de routesjablonen /{message:alpha} en /{message:int}:

  • De alpha beperking komt alleen overeen met alfabetische tekens.
  • De int beperking komt alleen overeen met getallen.
  • Deze sjablonen hebben dezelfde routeprioriteit, maar er is geen enkele URL die beide overeenkomen.
  • Als het routeringssysteem bij het opstarten een dubbelzinnigheidsfout heeft gerapporteerd, wordt deze geldige use-case geblokkeerd.

Warning

De volgorde van bewerkingen binnen UseEndpoints heeft geen invloed op het gedrag van routering, met één uitzondering. MapControllerRoute en MapAreaRoute wijs automatisch een orderwaarde toe aan hun eindpunten op basis van de volgorde die ze worden aangeroepen. Dit simuleert langdurige gedrag van controllers zonder dat het routeringssysteem dezelfde garanties biedt als oudere routeringsimplementaties.

Eindpuntroutering in ASP.NET Core:

  • Heeft geen concept van routes.
  • Biedt geen bestelgaranties. Alle eindpunten worden tegelijk verwerkt.

Prioriteit van routesjabloon en volgorde van eindpuntselectie

Prioriteit van routesjabloon is een systeem dat elke routesjabloon een waarde toewijst op basis van hoe specifiek het is. Prioriteit van routesjabloon:

  • Vermijdt de noodzaak om de volgorde van eindpunten in veelvoorkomende gevallen aan te passen.
  • Pogingen om overeen te komen met de algemene verwachtingen van routeringsgedrag.

Denk bijvoorbeeld aan sjablonen /Products/List en /Products/{id}. Het zou redelijk zijn om ervan uit te gaan dat /Products/List een betere overeenkomst is dan /Products/{id} voor het URL-pad /Products/List. Dit werkt omdat het letterlijke segment /List wordt beschouwd als een betere prioriteit dan het parametersegment /{id}.

De details van hoe prioriteit werkt, zijn gekoppeld aan de wijze waarop routesjablonen worden gedefinieerd:

  • Sjablonen met meer segmenten worden als specifieker beschouwd.
  • Een segment met letterlijke tekst wordt beschouwd als specifieker dan een parametersegment.
  • Een parametersegment met een beperking wordt beschouwd als specifieker dan één zonder.
  • Een complex segment wordt beschouwd als even specifiek als een parametersegment met een beperking.
  • Catch-all-parameters zijn het minst specifiek. Zie catch-all in de sectie Routesjablonen voor belangrijke informatie over catch-all routes.

Concepten voor het genereren van URL's

URL generation:

  • Is het proces waarmee routering een URL-pad kan maken op basis van een set routewaarden.
  • Hiermee is een logische scheiding mogelijk tussen eindpunten en de URL's die er toegang toe hebben.

Eindpuntroutering omvat de LinkGenerator API. LinkGenerator is een singleton-service die beschikbaar is via DI. De LinkGenerator API kan buiten de context van een uitvoeringsaanvraag worden gebruikt. Mvc.IUrlHelper en scenario's die afhankelijk zijn IUrlHelpervan, zoals Tag Helpers, HTML Helpers en Actieresultaten, gebruiken de LinkGenerator API intern om mogelijkheden voor het genereren van koppelingen te bieden.

De koppelingsgenerator wordt ondersteund door het concept van adres- en adresschema's. Een adresschema is een manier om de eindpunten te bepalen die moeten worden overwogen voor het genereren van koppelingen. De scenario's van routenaam en routewaarden die voor veel gebruikers bekend zijn, worden met controllers en Razor Pagina's geïmplementeerd als een adresschema.

De koppelingsgenerator kan via de volgende uitbreidingsmethoden een koppeling maken naar controllers en Razor pagina's:

Overbelastingen van deze methoden accepteren argumenten die de HttpContext. Deze methoden zijn functioneel gelijkwaardig aan Url.Action en Url.Page, maar bieden extra flexibiliteit en opties.

De GetPath* methoden zijn het meest vergelijkbaar met Url.Action en Url.Page, omdat ze een URI genereren die een absoluut pad bevat. De GetUri* methoden genereren altijd een absolute URI die een schema en host bevat. De methoden die een HttpContext accepteren, genereren een URI in de context van de lopende aanvraag. De omgevingsroutewaarden , het URL-basispad, het schema en de host van de uitvoerbare aanvraag worden gebruikt, tenzij deze worden overschreven.

LinkGenerator kan worden aangeroepen met een adres. Het genereren van een URI vindt plaats in twee stappen:

  1. Een adres is gebonden aan een lijst met eindpunten die overeenkomen met het adres.
  2. De eindpunten RoutePattern worden geëvalueerd totdat een routepatroon dat overeenkomt met de opgegeven waarden wordt gevonden. De resulterende uitvoer wordt gecombineerd met de andere URI-onderdelen die aan de koppelingsgenerator worden geleverd en geretourneerd.

De methoden die door LinkGenerator worden geboden, ondersteunen het genereren van standaardkoppelingen voor elk type adres. De handigste manier om de koppelingsgenerator te gebruiken, is via extensiemethoden die bewerkingen uitvoeren voor een specifiek adrestype:

Extension Method Description
GetPathByAddress Genereert een URI met een absoluut pad op basis van de opgegeven waarden.
GetUriByAddress Genereert een absolute URI op basis van de opgegeven waarden.

Warning

Let op de volgende gevolgen van het aanroepen LinkGenerator van methoden:

  • Gebruik GetUri* extensiemethoden met voorzichtigheid in een app-configuratie die de Host header van binnenkomende aanvragen niet valideert. Als de Host header van binnenkomende aanvragen niet wordt gevalideerd, kan niet-vertrouwde aanvraaginvoer worden teruggestuurd naar de client in URI's in een weergave of pagina. Het is raadzaam dat alle productie-apps hun server configureren om de Host header te valideren op basis van bekende geldige waarden.

  • Wees LinkGenerator voorzichtig met middleware in combinatie met Map of MapWhen. Map* wijzigt het basispad van de uitvoerende aanvraag, wat van invloed is op de uitvoer van linkgeneratie. Alle API's maken het mogelijk om LinkGenerator een basispad op te geven. Geef een leeg basispad op om het effect bij het Map* genereren van koppelingen ongedaan te maken.

Middleware example

In het volgende voorbeeld gebruikt een middleware de LinkGenerator API om een koppeling te maken naar een actiemethode met een lijst met winkelproducten. Het gebruik van de koppelingsgenerator door deze in een klasse te injecteren en aanroepen GenerateLink is beschikbaar voor elke klasse in een 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.");
    }
}

Route templates

Tokens binnen {} definiëren routeparameters die worden gebonden als de route overeenkomt. Er kunnen meer dan één routeparameter worden gedefinieerd in een routesegment, maar routeparameters moeten worden gescheiden door een letterlijke waarde. For example:

{controller=Home}{action=Index}

is geen geldige route, omdat er geen numerieke waarde tussen {controller} en {action} staat. Routeparameters moeten een naam hebben en kunnen aanvullende kenmerken hebben opgegeven.

Letterlijke tekst anders dan routeparameters (bijvoorbeeld {id}) en het padscheidingsteken / moet overeenkomen met de tekst in de URL. Tekstvergelijking is niet hoofdlettergevoelig en is gebaseerd op de gedecodeerde weergave van het pad van de URL. Als u een letterlijk routeparameterscheidingsteken { wilt vergelijken of }, escapet u het scheidingsteken door het teken te herhalen. Bijvoorbeeld {{ of }}.

Sterretje * of dubbel sterretje **:

  • Kan worden gebruikt als voorvoegsel voor een routeparameter om verbinding te maken met de rest van de URI.
  • Worden een catch-all-parameters genoemd. Bijvoorbeeld: blog/{**slug}
    • Komt overeen met elke URI die begint met blog/ en die een waarde bevat die erop volgt.
    • De waarde die volgt op blog/ wordt toegewezen aan de slug routewaarde.

Warning

Een catch-all--parameter kan verkeerde routes matchen door een fout in de routering. Apps die door deze fout worden beïnvloed, hebben de volgende kenmerken:

  • Een algemene route, bijvoorbeeld {**slug}"
  • De catch-all-route kan niet overeenkomen met aanvragen die moeten overeenkomen.
  • Als u andere routes verwijdert, begint de catch-all route te werken.

Zie GitHub-bugs 18677 en 16579 voor voorbeeldgevallen die deze bug ervaren.

Een opt-in-oplossing voor deze fout is opgenomen in .NET Core 3.1.301 of hoger SDK. Met de volgende code wordt een interne switch ingesteld waarmee deze fout wordt opgelost:

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

Catch-all-parameters kunnen ook overeenkomen met de lege tekenreeks.

De catch-all-parameter escapeert de juiste tekens wanneer de route wordt gebruikt om een URL te genereren, inclusief padscheidingstekens /. De route foo/{*path} met routewaarden { path = "my/path" } genereert foo/my%2Fpathbijvoorbeeld . Noteer de escaped slash. Gebruik het voorvoegsel van de ** routeparameter om het padscheidingsteken af te ronden. De route foo/{**path} met { path = "my/path" } genereert foo/my/path.

URL-patronen die proberen een bestandsnaam met een optionele bestandsextensie vast te leggen, hebben aanvullende overwegingen. Denk bijvoorbeeld aan de sjabloon files/{filename}.{ext?}. Wanneer waarden voor beide filename en ext bestaan, worden beide waarden ingevuld. Als er slechts een waarde voor filename bestaat in de URL, komt de route overeen omdat het volgtraject . optioneel is. De volgende URL's komen overeen met deze route:

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

Routeparameters kunnen standaardwaarden hebben die zijn aangewezen door de standaardwaarde op te geven na de parameternaam gescheiden door een gelijkteken (=). Definieert {controller=Home} bijvoorbeeld Home als de standaardwaarde voor controller. De standaardwaarde wordt gebruikt als er geen waarde aanwezig is in de URL voor de parameter. Routeparameters worden optioneel gemaakt door een vraagteken (?) toe te voegen aan het einde van de parameternaam. Bijvoorbeeld: id?. Het verschil tussen optionele waarden en standaardrouteparameters is:

  • Een routeparameter met een standaardwaarde produceert altijd een waarde.
  • Een optionele parameter heeft alleen een waarde wanneer een waarde wordt opgegeven door de aanvraag-URL.

Routeparameters kunnen beperkingen hebben die overeenkomen met de routewaarde die is gebonden aan de URL. Door : en de naam van de beperking na de naam van de routeparameter toe te voegen, wordt een inline beperking voor een routeparameter gespecificeerd. Als voor de beperking argumenten zijn vereist, staan deze tussen haakjes (...) achter de naam van de beperking. Er kunnen meerdere inlinebeperkingen worden opgegeven door een andere : naam en beperking toe te voegen.

De naam en argumenten van de beperking worden aan de IInlineConstraintResolver-service doorgegeven om een exemplaar van IRouteConstraint te maken, voor gebruik in URL-verwerking. De routesjabloon blog/{article:minlength(10)} geeft bijvoorbeeld een minlength beperking op met het argument 10. Zie de sectie Routebeperkingen voor meer informatie over routebeperkingen en een lijst met de beperkingen die door het framework worden geboden.

Routeparameters kunnen ook parametertransformatoren hebben. Parametertransformatoren transformeren de waarde van een parameter bij het genereren van koppelingen en overeenkomende acties en pagina's naar URL's. Net zoals beperkingen kunnen parametertransformatoren inline aan een routeparameter worden toegevoegd door na de naam van de routeparameter een : en de naam van de transformer toe te voegen. De routesjabloon blog/{article:slugify} geeft bijvoorbeeld een slugify transformator op. Zie de sectie Parametertransformatoren voor meer informatie over parametertransformaties .

In de volgende tabel ziet u voorbeeldroutesjablonen en hun gedrag:

Route Template Voorbeeld van overeenkomende URI De aanvraag-URI...
hello /hello Komt alleen overeen met het ene pad /hello.
{Page=Home} / Koppelt en stelt Page in op Home.
{Page=Home} /Contact Koppelt en stelt Page in op Contact.
{controller}/{action}/{id?} /Products/List Wordt toegewezen aan de Products controller en List actie.
{controller}/{action}/{id?} /Products/Details/123 Wordt toegewezen aan de controller Products en de actie Details, waarbij id is ingesteld op 123.
{controller=Home}/{action=Index}/{id?} / Verwijst naar de Home controller en Index methode. id wordt genegeerd.
{controller=Home}/{action=Index}/{id?} /Products Verwijst naar de Products controller en Index methode. id wordt genegeerd.

Het gebruik van een sjabloon is over het algemeen de eenvoudigste methode voor routering. Beperkingen en standaardwaarden kunnen ook buiten de routesjabloon worden opgegeven.

Complex segments

Complexe segmenten worden verwerkt door letterlijke scheidingstekens van rechts naar links op een niet-gretige wijze te vergelijken. Is bijvoorbeeld [Route("/a{b}c{d}")] een complex segment. Complexe segmenten werken op een bepaalde manier die moet worden begrepen om ze succesvol te kunnen gebruiken. In het voorbeeld in deze sectie ziet u waarom complexe segmenten alleen goed werken wanneer de tekst van het scheidingsteken niet binnen de parameterwaarden wordt weergegeven. Het gebruik van een regex en het handmatig extraheren van de waarden is nodig voor complexere gevallen.

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Dit is een samenvatting van de stappen die routering uitvoert met de sjabloon /a{b}c{d} en het URL-pad /abcd. De | functie wordt gebruikt om te visualiseren hoe het algoritme werkt:

  • De eerste letterlijke waarde, van rechts naar links, is c. Dus /abcd wordt vanaf rechts gezocht en vindt /ab|c|d.
  • Alles aan de rechterkant (d) komt nu overeen met de routeparameter {d}.
  • De volgende letterlijke waarde is a, gelezen van rechts naar links. Dus /ab|c|d wordt gezocht vanaf waar we gebleven waren, dan a wordt gevonden /|a|b|c|d.
  • De waarde aan de rechterkant (b) komt nu overeen met de routeparameter {b}.
  • Er is geen resterende tekst en geen resterende routesjabloon, dus dit komt overeen.

Hier volgt een voorbeeld van een negatief geval met dezelfde sjabloon /a{b}c{d} en het URL-pad /aabcd. De | functie wordt gebruikt om te visualiseren hoe het algoritme werkt. Dit geval is geen overeenkomst, wat wordt uitgelegd door hetzelfde algoritme:

  • De eerste letterlijke waarde, van rechts naar links, is c. Dus /aabcd wordt vanaf rechts gezocht en vindt /aab|c|d.
  • Alles aan de rechterkant (d) komt nu overeen met de routeparameter {d}.
  • De volgende letterlijke waarde is a, gelezen van rechts naar links. Dus /aab|c|d wordt gezocht vanaf waar we gebleven waren, dan a wordt gevonden /a|a|b|c|d.
  • De waarde aan de rechterkant (b) komt nu overeen met de routeparameter {b}.
  • Op dit moment is er nog tekst a, maar het algoritme heeft geen routesjabloon meer om te parseren, dus dit is geen overeenkomst.

Omdat het overeenkomende algoritme niet-graaiend is:

  • Deze komt overeen met de kleinste hoeveelheid tekst die in elke stap mogelijk is.
  • Elk geval waarin de scheidingstekenwaarde in de parameterwaarden wordt weergegeven, resulteert in niet-overeenkomende waarden.

Reguliere expressies bieden veel meer controle over hun overeenkomende gedrag.

Greedy matching, ook wel lazy matching genoemd, komt overeen met de grootst mogelijke tekenreeks. Niet-greedy komt overeen met de kleinste mogelijke tekenreeks.

Routeren met speciale tekens

Routering met speciale tekens kan leiden tot onverwachte resultaten. Denk bijvoorbeeld aan een controller met de volgende actiemethode:

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

Wanneer string id deze de volgende gecodeerde waarden bevat, kunnen onverwachte resultaten optreden:

ASCII Encoded
/ %2F
+

Routeparameters worden niet altijd gedecodeerd. Dit probleem kan in de toekomst worden opgelost. Zie dit GitHub-probleem voor meer informatie;

Route constraints

Routebeperkingen worden uitgevoerd wanneer er een overeenkomst is opgetreden met de binnenkomende URL en het URL-pad wordt omgezet in routewaarden. Routebeperkingen inspecteren doorgaans de routewaarde die via de routesjabloon is gekoppeld en bepalen of de waarde acceptabel is door een waar of onwaar beslissing te maken. Bij sommige routebeperkingen worden gegevens buiten de routewaarde gebruikt om na te gaan of de aanvraag kan worden gerouteerd. Bijvoorbeeld kan de HttpMethodRouteConstraint een aanvraag accepteren of weigeren op basis van het HTTP-werkwoord. Beperkingen worden gebruikt bij het genereren van routeringsaanvragen en het genereren van koppelingen.

Warning

Gebruik geen beperkingen voor invoervalidatie. Als beperkingen worden gebruikt voor invoervalidatie, resulteert ongeldige invoer in een 404 antwoord niet gevonden. Ongeldige invoer moet een 400 ongeldige aanvraag opleveren met een geschikt foutbericht. Routebeperkingen worden gebruikt om vergelijkbare routes te ontkoppelen, niet om de invoer voor een bepaalde route te valideren.

In de volgende tabel ziet u voorbeeld van routebeperkingen en hun verwachte gedrag:

constraint Example Example Matches Notes
int {id:int} 123456789, -123456789 Komt overeen met een geheel getal
bool {active:bool} true, FALSE Komt overeen met true of false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Komt overeen met een geldige DateTime waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
decimal {price:decimal} 49.99, -1,000.01 Komt overeen met een geldige decimal waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
double {weight:double} 1.234, -1,001.01e8 Komt overeen met een geldige double waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
float {weight:float} 1.234, -1,001.01e8 Komt overeen met een geldige float waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Komt overeen met een geldige Guid waarde
long {ticks:long} 123456789, -123456789 Komt overeen met een geldige long waarde
minlength(value) {username:minlength(4)} Rick Tekenreeks moet ten minste 4 tekens bevatten
maxlength(value) {filename:maxlength(8)} MyFile Tekenreeks mag niet langer zijn dan 8 tekens
length(length) {filename:length(12)} somefile.txt String moet exact 12 tekens lang zijn
length(min,max) {filename:length(8,16)} somefile.txt Een reeks moet minimaal 8 en niet meer dan 16 tekens lang zijn
min(value) {age:min(18)} 19 De waarde van het gehele getal moet ten minste 18 zijn
max(value) {age:max(120)} 91 Een geheel getal mag niet meer dan 120 zijn
range(min,max) {age:range(18,120)} 91 Een geheel getal moet ten minste 18 zijn, maar niet meer dan 120
alpha {name:alpha} Rick Een tekenreeks moet uit één of meer alfabetische tekens a-z bestaan en is hoofdletterongevoelig.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 De tekenreeks moet overeenkomen met de reguliere expressie. Zie tips voor het definiëren van een reguliere expressie.
required {name:required} Rick Wordt gebruikt om af te dwingen dat er een niet-parameterwaarde aanwezig is tijdens het genereren van de URL

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Meerdere, door dubbele punt gescheiden beperkingen kunnen worden toegepast op één parameter. Met de volgende beperking wordt bijvoorbeeld een parameter beperkt tot een geheel getal van 1 of hoger:

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

Warning

Routebeperkingen die de URL controleren en worden geconverteerd naar een CLR-type, gebruiken altijd de invariante cultuur. Bijvoorbeeld conversie naar het CLR-type int of DateTime. Bij deze beperkingen wordt ervan uitgegaan dat de URL niet kan worden gelokaliseerd. De door het framework geleverde routebeperkingen wijzigen niet de waarden die zijn opgeslagen in routewaarden. Alle routewaarden die uit de URL worden geparseerd, worden opgeslagen als tekenreeksen. De beperking probeert bijvoorbeeld float de routewaarde te converteren naar een float, maar de geconverteerde waarde wordt alleen gebruikt om te controleren of deze kan worden geconverteerd naar een float.

Reguliere expressies in beperkingen

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Reguliere expressies kunnen worden opgegeven als inlinebeperkingen met behulp van de regex(...) routebeperking. Methoden in de MapControllerRoute familie accepteren ook een object-literal met beperkingen. Als dit formulier wordt gebruikt, worden tekenreekswaarden geïnterpreteerd als reguliere expressies.

De volgende code maakt gebruik van een inline regex-beperking:

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

In de volgende code wordt een letterlijk object gebruikt om een regex-beperking op te geven:

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

Het ASP.NET Core-framework voegt RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant toe aan de reguliere expressie-constructor. Zie RegexOptions voor een beschrijving van deze leden.

Reguliere expressies maken gebruik van scheidingstekens en tokens die vergelijkbaar zijn met tokens die worden gebruikt door routering en de C#-taal. Tokens voor reguliere expressies moeten worden geëscaped. Als u de reguliere expressie ^\d{3}-\d{2}-\d{4}$ in een inlinebeperking wilt gebruiken, gebruikt u een van de volgende opties:

  • Vervang \ tekens in de tekenreeks als \\ tekens in het C#-bronbestand om het escapeteken voor tekenreeksen \ te escapen.
  • Verbatim tekenreeksen.

Als u tekens voor scheidingstekens van routeringsparameters wilt escapen, verdubbel dan de tekens in de expressie, bijvoorbeeld {, }, [, ], {{, }}, [[, ]]. In de volgende tabel ziet u een reguliere expressie en de escape-versie:

Regular expression Geëscapede reguliere expressie
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Reguliere expressies die in routering worden gebruikt, beginnen vaak met het ^ teken en komen overeen met de beginpositie van de tekenreeks. De expressies eindigen vaak met het $ teken en komen overeen met het einde van de tekenreeks. De ^ en $ tekens zorgen ervoor dat de reguliere expressie overeenkomt met de volledige waarde van de routeparameter. Zonder de ^ en $ tekens komt de reguliere expressie overeen met een subtekenreeks binnen de tekenreeks, wat vaak ongewenst is. De volgende tabel bevat voorbeelden en legt uit waarom ze overeenkomen of niet overeenkomen:

Expression String Match Comment
[a-z]{2} hello Yes Substring matches
[a-z]{2} 123abc456 Yes Substring matches
[a-z]{2} mz Yes Matches expression
[a-z]{2} MZ Yes Niet hoofdlettergevoelig
^[a-z]{2}$ hello No Zie ^ en $ hierboven
^[a-z]{2}$ 123abc456 No Zie ^ en $ hierboven

Zie .NET Framework Regular Expressions voor meer informatie over de syntaxis van reguliere expressies.

Als u een parameter wilt beperken tot een bekende set mogelijke waarden, gebruikt u een reguliere expressie. Komt bijvoorbeeld {action:regex(^(list|get|create)$)} alleen overeen met de action routewaarde naar list, getof create. Wanneer dit wordt doorgegeven aan een woordenlijst met beperkingen, is de tekenreeks ^(list|get|create)$ gelijk. Beperkingen die worden doorgegeven in de woordenlijst met beperkingen die niet overeenkomen met een van de bekende beperkingen, worden ook beschouwd als reguliere expressies. Beperkingen die worden doorgegeven binnen een sjabloon die niet overeenkomen met een van de bekende beperkingen, worden niet behandeld als reguliere expressies.

Aangepaste routebeperkingen

Aangepaste routebeperkingen kunnen worden gemaakt door de IRouteConstraint interface te implementeren. De IRouteConstraint interface bevat Match, die retourneert true als aan de beperking wordt voldaan en false anders.

Aangepaste routebeperkingen zijn zelden nodig. Voordat u een aangepaste routebeperking implementeert, moet u alternatieven overwegen, zoals modelbinding.

De map ASP.NET Kernbeperkingen biedt goede voorbeelden van het maken van beperkingen. Bijvoorbeeld GuidRouteConstraint.

Als u een aangepast type IRouteConstraintroutebeperking wilt gebruiken, moet het type routebeperking worden geregistreerd bij de app ConstraintMap in de servicecontainer. Een ConstraintMap is een woordenlijst die routebeperkingssleutels toe wijst aan IRouteConstraint implementaties die deze beperkingen valideren. ConstraintMap van een app kan in Program.cs worden bijgewerkt als onderdeel van een AddRouting-oproep of door RouteOptions rechtstreeks met builder.Services.Configure<RouteOptions> te configureren. For example:

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

De voorgaande beperking wordt toegepast in de volgende code:

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

De implementatie van NoZeroesRouteConstraint voorkomt dat 0 in een routeparameter wordt gebruikt:

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

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

De voorgaande code:

  • Voorkomt 0 in het {id} segment van de route.
  • Wordt weergegeven om een eenvoudig voorbeeld te geven van het implementeren van een aangepaste beperking. Deze mag niet worden gebruikt in een productie-app.

De volgende code is een betere benadering om te voorkomen dat een id met een 0 wordt verwerkt.

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

    return Content(id);
}

De voorgaande code heeft de volgende voordelen ten opzichte van de NoZeroesRouteConstraint aanpak:

  • Er is geen aangepaste beperking vereist.
  • Het retourneert een meer beschrijvende fout wanneer de routeparameter 0 bevat.

Parameter transformers

Parameter transformers:

Een aangepaste slugify parametertransformator in routepatroon blog\{article:slugify} met Url.Action(new { article = "MyTestArticle" }) genereert blog\my-test-articlebijvoorbeeld .

Houd rekening met de volgende IOutboundParameterTransformer implementatie:

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

Als u een parametertransformatie in een routepatroon wilt gebruiken, configureert u deze met behulp van ConstraintMapProgram.cs:

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

Het ASP.NET Core-framework gebruikt parametertransformatoren om de URI te transformeren waarop een eindpunt wordt afgerond. Met parametertransformatoren worden bijvoorbeeld de routewaarden getransformeerd die worden gebruikt om overeen te komen met een area, controller, action, en page.

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

Met de voorgaande routesjabloon wordt de actie SubscriptionManagementController.GetAll vergeleken met de URI /subscription-management/get-all. Een parametertransformator wijzigt niet de routewaarden die worden gebruikt om een koppeling te genereren. Bijvoorbeeld Url.Action("GetAll", "SubscriptionManagement") produceert /subscription-management/get-all.

ASP.NET Core biedt API-conventies voor het gebruik van parametertransformatoren met gegenereerde routes:

Naslaginformatie over het genereren van URL's

Deze sectie bevat een verwijzing voor het algoritme dat is geïmplementeerd door het genereren van url's. In de praktijk gebruiken de meeste complexe voorbeelden van het genereren van URL's controllers of Razor pagina's. Zie routering in controllers voor meer informatie.

Het proces voor het genereren van URL's begint met een aanroep naar LinkGenerator.GetPathByAddress of een vergelijkbare methode. De methode wordt geleverd met een adres, een set routewaarden en eventueel informatie over de huidige aanvraag van HttpContext.

De eerste stap is het gebruik van het adres om een set kandidaat-eindpunten op te lossen met behulp van een IEndpointAddressScheme<TAddress> eindpunt dat overeenkomt met het type adres.

Zodra de set kandidaten door het adresschema is gevonden, worden de eindpunten iteratief gerangschikt en verwerkt totdat een URL-generatiebewerking is geslaagd. Het genereren van url's controleert niet op dubbelzinnigheden. Het eerste resultaat dat wordt geretourneerd, is het uiteindelijke resultaat.

Problemen bij het genereren van URL's met logboekregistratie oplossen

De eerste stap bij het oplossen van problemen met URL-generatie is het instellen van het niveau van de logboekregistratie op Microsoft.AspNetCore.RoutingTRACE. LinkGenerator registreert veel details over de verwerking die nuttig kan zijn om problemen op te lossen.

Zie naslaginformatie over het genereren van URL's voor meer informatie over het genereren van url's.

Addresses

Adressen zijn het concept in het genereren van URL's dat wordt gebruikt om een aanroep aan de koppelingsgenerator te binden aan een set kandidaat-eindpunten.

Adressen zijn een uitbreidbaar concept dat standaard wordt geleverd met twee implementaties:

  • Gebruik de eindpuntnaam (string) als het adres:
    • Biedt vergelijkbare functionaliteit als de routenaam van MVC.
    • Maakt gebruik van het IEndpointNameMetadata metagegevenstype.
    • Hiermee wordt de opgegeven reeks afgestemd op de metagegevens van alle geregistreerde eindpunten.
    • Genereert een uitzondering bij het opstarten als meerdere eindpunten dezelfde naam gebruiken.
    • Aanbevolen voor algemeen gebruik buiten controllers en Razor pagina's.
  • Routewaarden (RouteValuesAddress) gebruiken als het adres:
    • Biedt vergelijkbare functionaliteit voor controllers en Razor pagina's voor het genereren van verouderde URL's.
    • Zeer complex om uit te breiden en fouten op te sporen.
    • Biedt de implementatie die wordt gebruikt door IUrlHelper, Tag Helpers, HTML Helpers, Actieresultaten, enzovoort.

De rol van het adresschema is het maken van de koppeling tussen het adres en overeenkomende eindpunten op willekeurige criteria:

  • Het eindpuntnaamschema voert een eenvoudige woordenboekopzoeking uit.
  • Het schema voor routewaarden heeft een complex algoritme voor het bepalen van de beste subset van een set.

Omgevingswaarden en expliciete waarden

Vanuit de huidige aanvraag heeft routering toegang tot de routewaarden van de huidige aanvraag HttpContext.Request.RouteValues. De waarden die aan de huidige aanvraag zijn gekoppeld, worden de omgevingswaarden genoemd. Ter duidelijkheid verwijst de documentatie naar de routewaarden die worden doorgegeven aan methoden als expliciete waarden.

In het volgende voorbeeld ziet u omgevingswaarden en expliciete waarden. Het biedt omgevingswaarden van de huidige aanvraag en expliciete waarden:

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

    // ...

De voorgaande code:

De volgende code bevat alleen expliciete waarden en geen omgevingswaarden:

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

De voorgaande methode retourneert /Home/Subscribe/17

De volgende code in de WidgetController retourneert /Widget/Subscribe/17:

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

De volgende code biedt de controller vanuit omgevingsvariabelen in het huidige verzoek en expliciete waarden:

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

In de voorgaande code:

  • /Gadget/Edit/17 wordt geretourneerd.
  • Url haalt de IUrlHelper.
  • Action genereert een URL met een absoluut pad voor een actiemethode. De URL bevat de opgegeven action naam en route waarden.

De volgende code bevat omgevingswaarden van de huidige aanvraag en expliciete waarden:

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

        // ...
    }
}

De voorgaande code stelt url in op /Edit/17 wanneer de pagina Bewerken Razor de volgende pagina-instructie bevat:

@page "{id:int}"

Als de pagina Bewerken de "{id:int}" routesjabloon niet bevat, is url/Edit?id=17.

Het gedrag van MVC's IUrlHelper voegt een complexiteitslaag toe, naast de regels die hier worden beschreven:

  • IUrlHelper levert altijd de routewaarden van de huidige aanvraag als omgevingswaarden.
  • IUrlHelper.Action kopieert altijd de huidige action en controller routewaarden als expliciete waarden, tenzij deze worden overschreven door de ontwikkelaar.
  • IUrlHelper.Page kopieert de huidige page routewaarde altijd als een expliciete waarde, tenzij deze wordt overschreven.
  • IUrlHelper.Page overschrijft altijd de huidige handler routewaarde met null als expliciete waarde, tenzij null wordt overschreven.

Gebruikers worden vaak verrast door de gedragsdetails van omgevingswaarden, omdat MVC geen eigen regels lijkt te volgen. Om historische en compatibiliteitsredenen hebben bepaalde routewaarden zoals action, controller, page, en handler hun eigen speciale gedrag.

De equivalente functionaliteit die wordt geleverd door LinkGenerator.GetPathByAction en LinkGenerator.GetPathByPage dupliceert deze afwijkingen van IUrlHelper voor compatibiliteit.

Proces voor het genereren van URL's

Zodra de set kandidaat-eindpunten is gevonden, genereert het algoritme URL's.

  • Hiermee worden de eindpunten iteratief verwerkt.
  • Retourneert het eerste geslaagde resultaat.

De eerste stap in dit proces wordt routewaardevervalsing genoemd. Routewaarde-invalidering is het proces waarmee routering bepaalt welke routewaarden van de ambiente waarden moeten worden gebruikt en welke moeten worden genegeerd. Elke omgevingswaarde wordt beschouwd en gecombineerd met de expliciete waarden of genegeerd.

De beste manier om na te denken over de rol van omgevingswaarden is dat ze proberen toepassingsontwikkelaars te helpen minder te typen in veelvoorkomende gevallen. Normaal gesproken zijn de scenario's waarin omgevingswaarden nuttig zijn gerelateerd aan MVC:

  • Wanneer u een koppeling maakt naar een andere actie in dezelfde controller, hoeft de controllernaam niet te worden opgegeven.
  • Wanneer u een koppeling maakt naar een andere controller in hetzelfde gebied, hoeft de gebiedsnaam niet te worden opgegeven.
  • Bij het koppelen aan dezelfde actiemethode hoeven routewaarden niet te worden opgegeven.
  • Wanneer u een koppeling maakt naar een ander deel van de app, wilt u geen routewaarden overdragen die geen betekenis hebben in dat deel van de app.

Aanroepen naar LinkGenerator of IUrlHelper die terug null geven, worden meestal veroorzaakt door het niet begrijpen van de invalidering van de routewaarde. Los ongeldige routewaarden op door expliciet meer routewaarden op te geven om te zien of het probleem hierdoor wordt opgelost.

Invalidatie van routewaarden werkt volgens de veronderstelling dat het URL-schema van de app hiërarchisch is, met een hiërarchie die van links naar rechts is gevormd. Overweeg de basissjabloon {controller}/{action}/{id?} voor controllerroute om een intuïtief beeld te krijgen van hoe dit in de praktijk werkt. Als u een waarde wijzigt , worden alle routewaarden die rechts worden weergegeven, ongeldig . Dit weerspiegelt de aanname over hiërarchie. Als de app een omgevingswaarde heeft voor id, en de bewerking een andere waarde specificeert voor de controller:

  • id wordt niet hergebruikt omdat {controller} links van {id?} staat.

Enkele voorbeelden waarin dit principe wordt gedemonstreerd:

  • Als de expliciete waarden een waarde bevatten voor id, wordt de omgevingswaarde genegeerd id . De omgevingswaarden voor controller en action kunnen worden gebruikt.
  • Als de expliciete waarden een waarde bevatten voor action, wordt een omgevingswaarde action voor genegeerd. De omgevingswaarden voor controller kunnen worden gebruikt. Als de expliciete waarde action anders is dan de omgevingswaarde, actionwordt de id waarde niet gebruikt. Als de expliciete waarde voor action hetzelfde is als de omgevingswaarde voor action, kan de id waarde worden gebruikt.
  • Als de expliciete waarden een waarde bevatten voor controller, wordt een omgevingswaarde controller voor genegeerd. Als de expliciete waarde controller verschilt van de omgevingswaarde voor controller, worden de action waarden id niet gebruikt. Als de expliciete waarde voor controller dezelfde is als de omgevingswaarde voor controller, kunnen de waarden action en id worden gebruikt.

Dit proces wordt verder gecompliceerd door het bestaan van kenmerkroutes en toegewezen conventionele routes. Conventionele routes, zoals {controller}/{action}/{id?}, geven een hiërarchie aan met behulp van routeparameters. Voor specifieke conventionele routes en attributieroutes naar controllers en Razor pagina's:

  • Er is een hiërarchie met routewaarden.
  • Ze worden niet weergegeven in de sjabloon.

In deze gevallen definieert het genereren van URL's het concept van de vereiste waarden . Eindpunten die zijn gemaakt door controllers en Razor pagina's hebben vereiste waarden opgegeven waarmee routewaardevervalidering kan worden uitgevoerd.

Het invalidatie-algoritme voor de routewaarde in detail:

  • De vereiste waardenamen worden gecombineerd met de routeparameters en worden vervolgens van links naar rechts verwerkt.
  • Voor elke parameter worden de omgevingswaarde en expliciete waarde vergeleken:
    • Als de omgevingswaarde en expliciete waarde hetzelfde zijn, wordt het proces voortgezet.
    • Als de omgevingswaarde aanwezig is en de expliciete waarde niet is, wordt de omgevingswaarde gebruikt bij het genereren van de URL.
    • Als de omgevingswaarde niet aanwezig is en de expliciete waarde is, negeert u de omgevingswaarde en alle volgende omgevingswaarden.
    • Als de omgevingswaarde en de expliciete waarde aanwezig zijn en de twee waarden verschillen, negeert u de omgevingswaarde en alle volgende omgevingswaarden.

Op dit moment is de bewerking voor het genereren van URL's gereed om routebeperkingen te evalueren. De set van geaccepteerde waarden wordt samen met de standaardwaarden van de parameter verstrekt aan de beperkingen. Als aan alle beperkingen is voldaan, wordt de bewerking voortgezet.

Vervolgens kunnen de geaccepteerde waarden worden gebruikt om de routesjabloon uit te vouwen. De routesjabloon wordt verwerkt:

  • From left-to-right.
  • Elke parameter wordt vervangen door de geaccepteerde waarde.
  • Met de volgende speciale gevallen:
    • Als er een waarde ontbreekt in de geaccepteerde waarden en de parameter een standaardwaarde heeft, wordt de standaardwaarde gebruikt.
    • Als er een waarde ontbreekt in de geaccepteerde waarden en de parameter optioneel is, wordt de verwerking voortgezet.
    • Als een routeparameter rechts van een ontbrekende optionele parameter een waarde heeft, mislukt de bewerking.
    • Aaneengesloten parameters met standaardwaarden en optionele parameters worden indien mogelijk samengevouwen.

Waarden die expliciet zijn opgegeven die niet overeenkomen met een segment van de route, worden toegevoegd aan de querytekenreeks. In de volgende tabel ziet u het resultaat wanneer u de routesjabloon {controller}/{action}/{id?}gebruikt.

Ambient Values Explicit Values Result
controller = "Home" action = "Info" /Home/About
controller = "Home" controller = "Order", action = "About" /Order/About
controller = "Home", color = "Rood" action = "Info" /Home/About
controller = "Home" actie = "Over", kleur = "Rood" /Home/About?color=Red

Optionele routeparametervolgorde

Optionele routeparameters moeten na alle vereiste routeparameters worden geleverd. In de volgende code moeten de id en name parameters na de color parameter worden geleverd:

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

Problemen met het ongeldig maken van routewaarde

De volgende code toont een voorbeeld van een URL-generatieschema dat niet wordt ondersteund door routering:

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

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

In de voorgaande code wordt de culture routeparameter gebruikt voor lokalisatie. De wens is om de culture parameter altijd als omgevingswaarde te laten accepteren. De culture parameter wordt echter niet geaccepteerd als een omgevingswaarde vanwege de manier waarop vereiste waarden werken:

  • In de "default" routesjabloon bevindt de culture routeparameter zich links van controller, zodat wijzigingen aan controllerculture niet ongeldig maken.
  • Binnen de "blog" routesjabloon wordt de culture routeparameter beschouwd als rechts van controller, die wordt weergegeven in de vereiste waarden.

URL-paden parseren met LinkParser

De LinkParser klasse voegt ondersteuning toe voor het parseren van een URL-pad in een set routewaarden. De ParsePathByEndpointName methode gebruikt een eindpuntnaam en een URL-pad en retourneert een set routewaarden die zijn geëxtraheerd uit het URL-pad.

In de volgende voorbeeldcontroller gebruikt de GetProduct actie een routesjabloon van api/Products/{id} en heeft een Name van GetProduct:

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

In dezelfde controllerklasse verwacht de AddRelatedProduct actie een URL-pad, pathToRelatedProductdat kan worden opgegeven als een queryreeksparameter:

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

    // ...

In het voorgaande voorbeeld haalt de AddRelatedProduct actie de id routewaarde op uit het URL-pad. Met een URL-pad van /api/Products/1bijvoorbeeld, wordt de relatedProductId waarde ingesteld op 1. Met deze benadering kunnen de CLIENTS van de API URL-paden gebruiken bij het verwijzen naar resources, zonder dat u hoeft te weten hoe een DERGELIJKE URL is gestructureerd.

Eindpuntmetagegevens configureren

De volgende koppelingen bevatten informatie over het configureren van eindpuntmetagegevens:

Hostkoppeling in routes met RequireHost

RequireHost past een beperking toe op de route waarvoor de opgegeven host is vereist. De RequireHost-parameter of de [Host] kan een:

  • Host: www.domain.com, komt www.domain.com overeen met elke poort.
  • Host met wildcard: *.domain.com, komt overeen met www.domain.com, subdomain.domain.com, of www.subdomain.domain.com op een willekeurige poort.
  • Poort: *:5000, komt overeen met poort 5000 met elke host.
  • Host en poort: www.domain.com:5000 of *.domain.com:5000, komt overeen met host en poort.

Er kunnen meerdere parameters worden opgegeven met of RequireHost[Host]. De beperking komt overeen met hosts die geldig zijn voor een van de parameters. Bijvoorbeeld, [Host("domain.com", "*.domain.com")] komt overeen met domain.com, www.domain.com, en subdomain.domain.com.

De volgende code gebruikt RequireHost om de opgegeven host op de route te vereisen:

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

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

De volgende code gebruikt het [Host] kenmerk op de controller om een van de opgegeven hosts te vereisen:

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

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

Wanneer het [Host] kenmerk wordt toegepast op zowel de controller als de actiemethode:

  • Het kenmerk van de actie wordt gebruikt.
  • Het controllerkenmerk wordt genegeerd.

Route groups

Met de MapGroup-extensiemethode kunt u groepen eindpunten organiseren met een gemeenschappelijk voorvoegsel. Het vermindert terugkerende code en maakt het mogelijk om hele groepen eindpunten aan te passen met één aanroep naar methoden zoals RequireAuthorization en WithMetadata waarmee metagegevens van eindpuntenworden toegevoegd.

Met de volgende code worden bijvoorbeeld twee vergelijkbare groepen eindpunten gemaakt:

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

In dit scenario kunt u een relatief adres gebruiken voor de Location-header in het 201 Created resultaat:

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

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

De eerste groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /public/todos en zijn toegankelijk zonder verificatie. De tweede groep eindpunten komt alleen overeen met aanvragen die zijn voorafgegaan door /private/todos en vereisen verificatie.

De QueryPrivateTodoseindpuntfilterfactory is een lokale functie waarmee de TodoDb parameters van de route-handler worden gewijzigd om toegang te krijgen tot privétodogegevens en deze op te slaan.

Routegroepen ondersteunen ook geneste groepen en complexe voorvoegselpatronen met routeparameters en beperkingen. In het volgende voorbeeld kan de routehandler die is toegewezen aan de user groep, de {org} en {group} routeparameters vastleggen die zijn gedefinieerd in de voorvoegsels van de buitenste groep.

Het voorvoegsel kan ook leeg zijn. Dit kan handig zijn voor het toevoegen van eindpuntmetagegevens of filters aan een groep eindpunten zonder het routepatroon te wijzigen.

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

Het toevoegen van filters of metagegevens aan een groep gedraagt zich op dezelfde manier als het afzonderlijk toevoegen van filters of metagegevens aan elk eindpunt voordat u extra filters of metagegevens toevoegt die mogelijk zijn toegevoegd aan een binnenste groep of specifiek eindpunt.

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

In het bovenstaande voorbeeld registreert het buitenste filter de binnenkomende aanvraag voordat het binnenste filter de kans krijgt om dat te doen, hoewel het binnenste filter als tweede is toegevoegd. Omdat de filters zijn toegepast op verschillende groepen, maakt de volgorde die ze ten opzichte van elkaar zijn toegevoegd niet uit. De volgorde waarin filters worden toegevoegd, maakt uit of ze op dezelfde groep of een specifiek eindpunt worden toegepast.

Een aanvraag voor /outer/inner/ meldt het volgende:

/outer group filter
/inner group filter
MapGet filter

Richtlijnen voor prestaties voor routering

Wanneer een app prestatieproblemen heeft, wordt routering vaak vermoed als het probleem. De reden waarom routering wordt vermoed, is dat frameworks zoals controllers en Razor Pagina's de hoeveelheid tijd rapporteren die binnen het framework is besteed in hun logboekberichten. Wanneer er een aanzienlijk verschil is tussen de tijd die door controllers wordt gerapporteerd en de totale tijd van de aanvraag:

  • Ontwikkelaars elimineren hun app-code als de bron van het probleem.
  • Het is gebruikelijk om aan te nemen dat routering de oorzaak is.

Routering wordt getest met behulp van duizenden eindpunten. Het is onwaarschijnlijk dat een typische app een prestatieprobleem ondervindt door te groot te zijn. De meest voorkomende hoofdoorzaak van trage routeringsprestaties is meestal een slecht gedragende aangepaste middleware.

In dit volgende codevoorbeeld ziet u een basistechniek voor het beperken van de bron van vertraging:

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

Voor tijdsplanning van routering:

  • Interleave elke middleware met een kopie van de timing-middleware die in de code hierboven wordt weergegeven.
  • Voeg een unieke id toe om de timinggegevens te correleren met de code.

Dit is een eenvoudige manier om de vertraging te beperken wanneer deze aanzienlijk is, bijvoorbeeld meer dan 10ms. Het aftrekken van Time 2 van Time 1 geeft de tijd weer die binnen de UseRouting middleware is besteed.

De volgende code maakt gebruik van een compactere benadering van de voorgaande tijdscode:

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

Mogelijk dure routeringsfuncties

De volgende lijst biedt inzicht in routeringsfuncties die relatief duur zijn vergeleken met basisroutesjablonen:

  • Reguliere expressies: het is mogelijk om reguliere expressies te schrijven die complex zijn of langlopende tijd hebben met een kleine hoeveelheid invoer.
  • Complexe segmenten ({x}-{y}-{z}):
    • Zijn aanzienlijk duurder dan het parseren van een normaal URL-padsegment.
    • Resulteert in veel meer subtekenreeksen die toegewezen worden.
  • Synchrone gegevenstoegang: veel complexe apps hebben databasetoegang als onderdeel van hun routering. Gebruik uitbreidbaarheidspunten zoals MatcherPolicy en EndpointSelectorContext, die asynchroon zijn.

Richtlijnen voor grote routetabellen

Standaard gebruikt ASP.NET Core een routeringsalgoritmen die geheugen ruilt voor CPU-tijd. Dit heeft het mooie effect dat routekoppelingstijd alleen afhankelijk is van de lengte van het pad dat moet worden vergeleken en niet het aantal routes. Deze benadering kan echter in sommige gevallen problematisch zijn, wanneer de app een groot aantal routes (in duizenden) heeft en er een grote hoeveelheid variabele voorvoegsels in de routes is. Als de routes bijvoorbeeld parameters hebben in vroege segmenten van de route, zoals {parameter}/some/literal.

Het is onwaarschijnlijk dat een app een situatie ondervindt waarbij dit een probleem is, tenzij:

  • Er is een groot aantal routes in de app met behulp van dit patroon.
  • Er is een groot aantal routes in de app.

Bepalen of een app het probleem met de grote routetabel ondervindt

  • Er zijn twee symptomen om naar te zoeken:
    • De app wordt langzaam gestart bij de eerste aanvraag.
      • Houd er rekening mee dat dit vereist is, maar niet voldoende. Er zijn veel andere niet-routeproblemen die kunnen leiden tot tragere opstart van apps. Controleer de onderstaande voorwaarde om nauwkeurig te bepalen of de app in deze situatie terechtkomt.
    • De app verbruikt veel geheugen tijdens het opstarten en een geheugendump toont een groot aantal Microsoft.AspNetCore.Routing.Matching.DfaNode exemplaren.

Dit probleem oplossen

Er zijn verschillende technieken en optimalisaties die kunnen worden toegepast op routes die dit scenario grotendeels verbeteren:

  • Pas waar mogelijk routebeperkingen toe op uw parameters, bijvoorbeeld {parameter:int}{parameter:guid}, {parameter:regex(\\d+)}enzovoort.
    • Hierdoor kan het routeringsalgoritme intern de structuren optimaliseren die worden gebruikt voor het vergelijken en drastisch verminderen van het gebruikte geheugen.
    • In de overgrote meerderheid van de gevallen volstaat dit om terug te keren naar een aanvaardbaar gedrag.
  • Wijzig de routes om parameters naar latere segmenten in de sjabloon te verplaatsen.
    • Het vermindert het aantal mogelijke paden dat overeenkomt met een eindpunt, gegeven een bepaald pad.
  • Gebruik een dynamische route en voer de koppeling dynamisch uit naar een controller/pagina.
    • Dit kan worden bereikt met behulp van MapDynamicControllerRoute en MapDynamicPageRoute.

Richtlijnen voor bibliotheekauteurs

Deze sectie bevat richtlijnen voor auteurs van bibliotheken die voortbouwen op routering. Deze details zijn bedoeld om ervoor te zorgen dat app-ontwikkelaars een goede ervaring hebben met het gebruik van bibliotheken en frameworks waarmee routering wordt uitgebreid.

Define endpoints

Als u een framework wilt maken dat gebruikmaakt van routing voor URL-koppeling, begint u met het definiëren van een gebruikerservaring die voortbouwt op UseEndpoints.

DO bovenop IEndpointRouteBuilder bouwen. Hierdoor kunnen gebruikers uw framework opstellen met andere ASP.NET Core-functies zonder verwarring. Elke ASP.NET Core-sjabloon bevat routering. Stel dat routering aanwezig is en bekend is voor gebruikers.

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

app.MapHealthChecks("/healthz");

DO retourneert een verzegeld betontype van een aanroep naar MapMyFramework(...) dat implementeert IEndpointConventionBuilder. De meeste frameworkmethoden Map... volgen dit patroon. De IEndpointConventionBuilder interface:

  • Hiermee kunnen metagegevens worden samengesteld.
  • Is gericht op verschillende uitbreidingsmethoden.

Door uw eigen type te declareren, kunt u uw eigen frameworkspecifieke functionaliteit toevoegen aan de opbouwfunctie. Het is prima om een door het framework gedeclareerde bouwer in te pakken en oproepen door te sturen.

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

app.MapHealthChecks("/healthz");

OVERWEEG om je eigen EndpointDataSourcete schrijven. EndpointDataSource is het primitieve laag-niveau mechanisme voor het declareren en bijwerken van een verzameling eindpunten. EndpointDataSource is een krachtige API die wordt gebruikt door controllers en Razor Pages.

De routeringstests hebben een eenvoudig voorbeeld van een gegevensbron die niet wordt bijgewerkt.

OVERWEEG om te implementeren GetGroupedEndpoints. Dit geeft volledige controle over het uitvoeren van groepsconventies en de uiteindelijke metagegevens op de gegroepeerde eindpunten. Hierdoor kunnen aangepaste EndpointDataSource implementaties bijvoorbeeld eindpuntfilters uitvoeren die zijn toegevoegd aan groepen.

PROBEER NIET standaard een EndpointDataSource registratie uit te voeren. Vereisen dat gebruikers uw framework registreren in UseEndpoints. De filosofie van routering is dat er standaard niets is inbegrepen en dat UseEndpoints is de plek om eindpunten te registreren.

Routerings-geïntegreerde middleware maken

OVERWEEG metagegevenstypen te definiëren als een interface.

DO maakt het mogelijk om metagegevenstypen te gebruiken als een kenmerk voor klassen en methoden.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

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

Frameworks zoals controllers en Razor pagina's ondersteunen het toepassen van metagegevenskenmerken op typen en methoden. Als u metagegevenstypen declareert:

  • Maak ze toegankelijk als kenmerken.
  • De meeste gebruikers zijn bekend met het toepassen van kenmerken.

Door een metagegevenstype als interface te declareren, voegt u een andere flexibiliteitslaag toe:

  • Interfaces zijn samenstelbaar.
  • Ontwikkelaars kunnen hun eigen typen declareren die meerdere beleidsregels combineren.

DO maakt het mogelijk om metagegevens te overschrijven, zoals wordt weergegeven in het volgende voorbeeld:

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

De beste manier om deze richtlijnen te volgen, is om te voorkomen dat metagegevens van markeringen worden gedefinieerd:

  • Zoek niet alleen naar de aanwezigheid van een metagegevenstype.
  • Definieer een eigenschap op de metagegevens en controleer de eigenschap.

De verzameling metagegevens wordt gerangschikt en ondersteunt het overschrijven op prioriteit. In het geval van controllers zijn metagegevens voor de actiemethode het meest specifiek.

Maak middleware nuttig met en zonder routering:

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

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

Bekijk als voorbeeld van deze richtlijn de UseAuthorization middleware. Met de middleware voor autorisatie kunt u een terugvalbeleid doorgeven. Het terugvalbeleid, indien opgegeven, is van toepassing op beide:

  • Eindpunten zonder een opgegeven beleid.
  • Aanvragen die niet overeenkomen met een eindpunt.

Dit maakt de autorisatie-middleware nuttig buiten de context van routering. De autorisatie-middleware kan worden gebruikt voor traditionele middlewareprogrammering.

Debug diagnostics

Voor gedetailleerde routeringsdiagnose-uitvoer, stel Logging:LogLevel:Microsoft in op Debug. Stel in de ontwikkelomgeving het logboekniveau in:appsettings.Development.json

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

Additional resources

Routering is verantwoordelijk voor het koppelen van binnenkomende HTTP-aanvragen en het verzenden van deze aanvragen naar de uitvoerbare eindpunten van de app. Eindpunten zijn de eenheden van uitvoerbare aanvraagafhandelingscode van de app. Eindpunten worden gedefinieerd in de app en geconfigureerd wanneer de app wordt gestart. Het overeenkomende eindpuntproces kan waarden extraheren uit de URL van de aanvraag en deze waarden opgeven voor het verwerken van aanvragen. Met behulp van eindpuntgegevens van de app kan routering ook URL's genereren die zijn toegewezen aan eindpunten.

Apps kunnen routering configureren met behulp van:

In dit artikel worden details van ASP.NET Core-routering op laag niveau beschreven. Voor informatie over het configureren van routering:

Routing basics

De volgende code toont een basisvoorbeeld van routering:

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

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

app.Run();

Het voorgaande voorbeeld bevat één eindpunt met behulp van de MapGet methode:

  • Wanneer een HTTP-aanvraag GET naar de hoofd-URL /wordt verzonden:
    • De gemachtigde van de aanvraag wordt uitgevoerd.
    • Hello World! wordt naar het HTTP-antwoord geschreven.
  • Als de aanvraagmethode niet GET is of de hoofd-URL niet /is, worden er geen routeovereenkomsten en wordt een HTTP 404 geretourneerd.

Routering maakt gebruik van een paar middleware, geregistreerd door UseRouting en UseEndpoints:

  • UseRouting voegt routekoppeling toe aan de middleware-pijplijn. Deze middleware bekijkt de set eindpunten die in de app zijn gedefinieerd en selecteert de beste overeenkomst op basis van de aanvraag.
  • UseEndpoints voegt eindpuntuitvoering toe aan de middleware-pijplijn. Het voert de delegate uit die is gekoppeld aan het geselecteerde eindpunt.

Apps hoeven meestal niet UseRouting of UseEndpoints aan te roepen. WebApplicationBuilder configureert een middleware-pijplijn die middleware die is toegevoegd in Program.cs omhult met UseRouting en UseEndpoints. Apps kunnen echter de volgorde wijzigen waarin UseRouting en UseEndpoints worden uitgevoerd door deze methoden expliciet aan te roepen. Met de volgende code wordt bijvoorbeeld een expliciete aanroep uitgevoerd naar UseRouting:

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

app.UseRouting();

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

In de voorgaande code:

  • Het aanroepen van app.Use registreert een aangepaste middleware die aan het begin van de pijplijn wordt uitgevoerd.
  • De aanroep voor UseRouting het configureren van de route die overeenkomt met middleware die moet worden uitgevoerd na de aangepaste middleware.
  • Het eindpunt dat is geregistreerd bij MapGet wordt aan het einde van de pijplijn uitgevoerd.

Als er in het voorgaande voorbeeld geen aanroep van UseRouting was opgenomen, zou de aangepaste middleware worden uitgevoerd na de route-matchende middleware.

Endpoints

De MapGet methode wordt gebruikt om een eindpunt te definiëren. Een eindpunt is iets dat het volgende kan zijn:

  • Geselecteerd door de URL en HTTP-methode te koppelen.
  • Uitgevoerd door de gemachtigde uit te voeren.

Eindpunten die door de app kunnen worden vergeleken en uitgevoerd, worden geconfigureerd in UseEndpoints. MapGet MapPost Vergelijkbare methoden verbinden aanvraagafgevaardigden met het routeringssysteem. Aanvullende methoden kunnen worden gebruikt om ASP.NET Core-frameworkfuncties te verbinden met het routeringssysteem:

In het volgende voorbeeld ziet u routering met een geavanceerdere routesjabloon:

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

De tekenreeks /hello/{name:alpha} is een routesjabloon. Er wordt een routesjabloon gebruikt om te configureren hoe het eindpunt wordt gekoppeld. In dit geval komt de sjabloon overeen met:

  • Een URL zoals /hello/Docs
  • Elk URL-pad dat begint met /hello/ gevolgd door een reeks alfabetische tekens. :alpha past een routebeperking toe die alleen overeenkomt met alfabetische tekens. Routebeperkingen worden verderop in dit artikel uitgelegd.

Het tweede segment van het URL-pad: {name:alpha}

In het volgende voorbeeld ziet u routering met statuscontroles en autorisatie:

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

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

In het voorgaande voorbeeld ziet u hoe:

  • De middleware voor autorisatie kan worden gebruikt met routering.
  • Eindpunten kunnen worden gebruikt om autorisatiegedrag te configureren.

De MapHealthChecks aanroep voegt een statuscontrole-eindpunt toe. Door RequireAuthorization aan deze oproep toe te voegen, wordt een autorisatiebeleid aan het eindpunt toegevoegd.

Door UseAuthentication en UseAuthorization aan te roepen, wordt de verificatie- en autorisatie-middleware toegevoegd. Deze middleware worden geplaatst tussen UseRouting en UseEndpoints zodat ze het volgende kunnen:

  • Zien welk eindpunt is geselecteerd door UseRouting.
  • Pas een autorisatiebeleid toe voordat UseEndpoints naar het eindpunt wordt verzonden.

Endpoint metadata

In het voorgaande voorbeeld zijn er twee eindpunten, maar alleen het eindpunt voor de statuscontrole heeft een autorisatiebeleid gekoppeld. Als de aanvraag overeenkomt met het eindpunt van de statuscontrole, /healthzwordt er een autorisatiecontrole uitgevoerd. Dit laat zien dat er extra gegevens aan eindpunten kunnen worden gekoppeld. Deze extra gegevens worden eindpuntmetagegevens genoemd:

  • De metagegevens kunnen worden verwerkt door middel van routeringsbewuste middleware.
  • De metagegevens kunnen van elk .NET-type zijn.

Routing concepts

Het routeringssysteem bouwt voort op de middleware-pijplijn door het krachtige eindpuntconcept toe te voegen. Eindpunten vertegenwoordigen eenheden van de functionaliteit van de app die van elkaar verschillen wat betreft routering, autorisatie en een willekeurig aantal ASP.NET Core-systemen.

ASP.NET Kerneindpuntdefinitie

Een ASP.NET Core-eindpunt is:

De volgende code laat zien hoe u het eindpunt ophaalt en inspecteert dat overeenkomt met de huidige aanvraag:

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

Het eindpunt, indien geselecteerd, kan worden opgehaald uit de HttpContext. De eigenschappen kunnen worden geïnspecteerd. Eindpuntobjecten zijn onveranderbaar en kunnen niet worden gewijzigd na het maken. Het meest voorkomende type eindpunt is een RouteEndpoint. RouteEndpoint bevat informatie waarmee deze kan worden geselecteerd door het routeringssysteem.

In de voorgaande code configureert app.Use een inline-middleware.

In de volgende code blijkt dat er, afhankelijk van waar in de pijplijn app.Use wordt aangeroepen, mogelijk geen eindpunt is.

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

In het voorgaande voorbeeld worden instructies toegevoegd Console.WriteLine die aangeven of er al dan niet een eindpunt is geselecteerd. Ter duidelijkheid wijst het voorbeeld een weergavenaam toe aan het opgegeven / eindpunt.

Het voorgaande voorbeeld bevat ook aanroepen naar UseRouting en UseEndpoints om precies te bepalen wanneer deze middleware in de pijplijn wordt uitgevoerd.

Als u deze code uitvoert met een URL van /, wordt:

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

Als u deze code uitvoert met een andere URL, wordt het volgende weergegeven:

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

Deze uitvoer laat zien dat:

  • Het eindpunt is altijd null voordat UseRouting wordt aangeroepen.
  • Als er een overeenkomst wordt gevonden, is het eindpunt niet null tussen UseRouting en UseEndpoints.
  • De UseEndpoints middleware is terminal wanneer er een overeenkomst wordt gevonden. Terminal middleware wordt verderop in dit artikel gedefinieerd.
  • Na UseEndpoints wordt de middleware alleen uitgevoerd als er geen overeenkomst wordt gevonden.

De UseRouting middleware gebruikt de SetEndpoint methode om het eindpunt aan de huidige context te koppelen. Het is mogelijk om de UseRouting middleware te vervangen door aangepaste logica en nog steeds de voordelen te krijgen van het gebruik van eindpunten. Eindpunten zijn een laag-niveau primitief zoals middleware en zijn niet gekoppeld aan de routeringsimplementatie. De meeste apps hoeven UseRouting niet te vervangen door aangepaste logica.

De UseEndpoints middleware is ontworpen om te worden gebruikt in combinatie met de UseRouting middleware. De kernlogica voor het uitvoeren van een eindpunt is niet ingewikkeld. Gebruik GetEndpoint om het eindpunt op te halen en roep vervolgens de eigenschap van RequestDelegate aan.

De volgende code laat zien hoe middleware invloed kan hebben op of kan reageren op routering:

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

In het voorgaande voorbeeld ziet u twee belangrijke concepten:

  • Middleware kan worden uitgevoerd voordat UseRouting om de gegevens waarop routering plaatsvindt te wijzigen.
  • Middleware kan worden uitgevoerd tussen UseRouting en UseEndpoints om de resultaten van routering te verwerken voordat het eindpunt wordt uitgevoerd.
    • Middleware die wordt uitgevoerd tussen UseRouting en UseEndpoints:
      • Inspecteert meestal metagegevens om inzicht te hebben in de eindpunten.
      • Vaak neemt u beveiligingsbeslissingen, zoals gedaan door UseAuthorization en UseCors.
    • Met de combinatie van middleware en metagegevens kunt u beleidsregels per eindpunt configureren.

In de voorgaande code ziet u een voorbeeld van een aangepaste middleware die beleid per eindpunt ondersteunt. De middleware schrijft een auditlog van de toegang tot gevoelige gegevens naar de console. De middleware kan worden geconfigureerd om een eindpunt te controleren met de RequiresAuditAttribute metagegevens. In dit voorbeeld ziet u een opt-in-patroon waarbij alleen eindpunten die als gevoelig zijn gemarkeerd, worden gecontroleerd. Het is mogelijk om deze logica omgekeerd te definiëren en alles te controleren dat niet als veilig is gemarkeerd, bijvoorbeeld. Het eindpuntmetagegevenssysteem is flexibel. Deze logica kan op welke manier dan ook worden ontworpen voor de use-case.

De voorgaande voorbeeldcode is bedoeld om de basisconcepten van eindpunten te demonstreren. Het voorbeeld is niet bedoeld voor productiegebruik. Een volledigere versie van een middleware voor auditlogboeken zou het volgende doen:

  • Meld u aan bij een bestand of database.
  • Neem details op, zoals de gebruiker, het IP-adres, de naam van het gevoelige eindpunt en meer.

De metagegevens RequiresAuditAttribute van het controlebeleid worden gedefinieerd als een Attribute voor eenvoudiger gebruik met frameworks op basis van klassen, zoals controllers en SignalR. Wanneer u route naar code gebruikt:

  • Metagegevens worden gekoppeld aan een builder-API.
  • Frameworks op basis van klassen bevatten alle kenmerken van de bijbehorende methode en klasse bij het maken van eindpunten.

De aanbevolen procedures voor metagegevenstypen zijn om ze te definiëren als interfaces of kenmerken. Interfaces en kenmerken staan hergebruik van code toe. Het metagegevenssysteem is flexibel en legt geen beperkingen op.

Terminal-middleware vergelijken met routering

In het volgende voorbeeld ziet u zowel terminal-middleware als routering:

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

De stijl van middleware die wordt weergegeven met Approach 1: is terminal-middleware. Dit wordt terminal-middleware genoemd omdat er een overeenkomende bewerking wordt uitgevoerd:

  • De overeenkomende bewerking in het voorgaande voorbeeld is Path == "/" voor de middleware en Path == "/Routing" voor routering.
  • Wanneer een overeenkomst succesvol is, wordt bepaalde functionaliteit uitgevoerd en wordt er teruggekeerd zonder dat de next middleware wordt aangeroepen.

Dit wordt terminal middleware genoemd omdat de zoekopdracht wordt beëindigd, bepaalde functionaliteit wordt uitgevoerd en vervolgens wordt geretourneerd.

De volgende lijst vergelijkt terminal-middleware met routering:

  • Beide benaderingen maken het beëindigen van de verwerkingspijplijn mogelijk:
    • Middleware beëindigt de pijplijn door te retourneren in plaats van aan te nextroepen.
    • Eindpunten zijn altijd terminal.
  • Met terminal-middleware kunt u de middleware op een willekeurige plaats in de pijplijn plaatsen:
    • Eindpunten worden uitgevoerd op de positie van UseEndpoints.
  • Met terminal-middleware kan willekeurige code worden bepaald wanneer de middleware overeenkomt:
    • Aangepaste routingcode kan langdradig zijn en moeilijk correct te schrijven.
    • Routering biedt eenvoudige oplossingen voor typische apps. Voor de meeste apps is geen aangepaste routekoppelingscode vereist.
  • Eindpuntinterface met middleware zoals UseAuthorization en UseCors.
    • Het gebruik van een terminal-middleware met UseAuthorization of UseCors vereist handmatige interfacing met het autorisatiesysteem.

Een eindpunt definieert beide:

  • Een gemachtigde voor het verwerken van aanvragen.
  • Een verzameling willekeurige metagegevens. De metagegevens worden gebruikt om kruislingse problemen te implementeren op basis van beleidsregels en configuratie die aan elk eindpunt zijn gekoppeld.

Terminal-middleware kan een effectief hulpprogramma zijn, maar kan het volgende vereisen:

  • Een aanzienlijke hoeveelheid coderen en testen.
  • Handmatige integratie met andere systemen om het gewenste flexibiliteitsniveau te bereiken.

Overweeg om te integreren met routering voordat u een terminal-middleware schrijft.

Bestaande terminal-middleware die kan worden geïntegreerd met Kaart of MapWhen kan meestal worden omgezet in een routeringsbewust eindpunt. MapHealthChecks demonstreert het patroon voor router-ware:

  • Schrijf een extensiemethode op IEndpointRouteBuilder.
  • Maak een geneste middleware-pijplijn met behulp van CreateApplicationBuilder.
  • Koppel de middleware aan de nieuwe pijplijn. In dit geval UseHealthChecks.
  • Build de middleware-pijplijn naar een RequestDelegate.
  • Roep Map aan en geef de nieuwe middleware-pijplijn op.
  • Het opbouwobject dat door de extensiemethode Map wordt geleverd, wordt geretourneerd.

De volgende code toont het gebruik van MapHealthChecks:

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

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

In het voorgaande voorbeeld ziet u waarom het retourneren van het opbouwobject belangrijk is. Als het opbouwobject wordt geretourneerd, kan de app-ontwikkelaar beleidsregels configureren, zoals autorisatie voor het eindpunt. In dit voorbeeld heeft de gezondheidscontroles middleware geen directe integratie met het autorisatiesysteem.

Het metagegevenssysteem is gemaakt als reactie op de problemen die zijn opgetreden door auteurs van uitbreidbaarheid met behulp van terminal-middleware. Het is problematisch voor elke middleware om een eigen integratie met het autorisatiesysteem te implementeren.

URL matching

  • Is het proces waarmee routering overeenkomt met een binnenkomende aanvraag naar een eindpunt.
  • Is gebaseerd op gegevens in het URL-pad en de headers.
  • Kan worden uitgebreid om rekening te houden met alle gegevens in de aanvraag.

Wanneer een routing-middleware wordt uitgevoerd, worden een Endpoint en routewaarden ingesteld op een verzoekfunctie van het HttpContext huidige verzoek:

Middleware wordt uitgevoerd nadat de routerings-middleware het eindpunt kan inspecteren en actie kan ondernemen. Een autorisatie-middleware kan bijvoorbeeld de metagegevensverzameling van het eindpunt ondervragen voor een autorisatiebeleid. Nadat alle middleware in de pijplijn voor aanvraagverwerking is uitgevoerd, wordt de gemachtigde van het geselecteerde eindpunt aangeroepen.

Het routeringssysteem in eindpuntroutering is verantwoordelijk voor alle verzendbeslissingen. Omdat de middleware beleidsregels toepast op basis van het geselecteerde eindpunt, is het belangrijk dat:

  • Elke beslissing die van invloed kan zijn op verzending of de toepassing van beveiligingsbeleid wordt gemaakt in het routeringssysteem.

Warning

Voor achterwaartse compatibiliteit, wanneer een controller- of Razor pagina's eindpuntdelegatie wordt uitgevoerd, worden de eigenschappen van RouteContext.RouteData ingesteld op de juiste waarden op basis van de verwerking van de aanvraag tot nu toe.

Het RouteContext type wordt gemarkeerd als verouderd in een toekomstige release:

  • Migreren RouteData.Values naar HttpRequest.RouteValues.
  • Migreer RouteData.DataTokens om IDataTokensMetadata uit de eindpuntmetagegevens op te halen.

URL-vergelijking werkt in een configureerbare set fasen. In elke fase is de uitvoer een verzameling van overeenkomsten. De reeks wedstrijden kan verder worden beperkt door de volgende fase. De implementatie van routering garandeert geen verwerkingsvolgorde voor overeenkomende eindpunten. Alle mogelijke overeenkomsten worden in één keer verwerkt. De fasen van het overeenkomen van de URL vinden plaats in de volgende volgorde. ASP.NET Core:

  1. Verwerkt het URL-pad op basis van de set eindpunten en de bijbehorende routesjablonen, en verzamelt alle overeenkomsten.
  2. Neemt de voorgaande lijst, en verwijdert overeenkomsten die niet voldoen aan de toegepaste routebeperkingen.
  3. Neemt de voorgaande lijst en verwijdert overeenkomsten die falen in de set MatcherPolicy instanties.
  4. EndpointSelector Hiermee kunt u een definitieve beslissing nemen uit de voorgaande lijst.

De lijst met eindpunten wordt gerangschikt op basis van:

Alle overeenkomende eindpunten worden in elke fase verwerkt totdat het EndpointSelector is bereikt. Het EndpointSelector is de laatste fase. Het systeem kiest het eindpunt met de hoogste prioriteit van de overeenkomsten als beste overeenkomst. Als er andere overeenkomsten zijn met dezelfde prioriteit als de beste overeenkomst, wordt er een dubbelzinnige match-exceptie opgeworpen.

De routeprioriteit wordt berekend op basis van een specifiekere routesjabloon die een hogere prioriteit krijgt. Denk bijvoorbeeld aan de sjablonen /hello en /{message}:

  • Beide komen overeen met het URL-pad /hello.
  • /hello is specifieker en dus hogere prioriteit.

Over het algemeen doet routeprioriteit goed werk bij het kiezen van de beste match voor de soorten URL-schema's die in de praktijk worden gebruikt. Gebruik Order deze functie alleen wanneer dat nodig is om dubbelzinnigheid te voorkomen.

Vanwege de soorten uitbreidbaarheid die wordt geboden door routering, is het niet mogelijk dat het routeringssysteem van tevoren de ambigu routes berekent. Bekijk een voorbeeld zoals de routesjablonen /{message:alpha} en /{message:int}:

  • De alpha beperking komt alleen overeen met alfabetische tekens.
  • De int beperking komt alleen overeen met getallen.
  • Deze sjablonen hebben dezelfde routeprioriteit, maar er is geen enkele URL die beide overeenkomen.
  • Als het routeringssysteem bij het opstarten een dubbelzinnigheidsfout heeft gerapporteerd, wordt deze geldige use-case geblokkeerd.

Warning

De volgorde van bewerkingen binnen UseEndpoints heeft geen invloed op het gedrag van routering, met één uitzondering. MapControllerRoute en MapAreaRoute wijs automatisch een orderwaarde toe aan hun eindpunten op basis van de volgorde die ze worden aangeroepen. Dit simuleert langdurige gedrag van controllers zonder dat het routeringssysteem dezelfde garanties biedt als oudere routeringsimplementaties.

Eindpuntroutering in ASP.NET Core:

  • Heeft geen concept van routes.
  • Biedt geen bestelgaranties. Alle eindpunten worden tegelijk verwerkt.

Prioriteit van routesjabloon en volgorde van eindpuntselectie

Prioriteit van routesjabloon is een systeem dat elke routesjabloon een waarde toewijst op basis van hoe specifiek het is. Prioriteit van routesjabloon:

  • Vermijdt de noodzaak om de volgorde van eindpunten in veelvoorkomende gevallen aan te passen.
  • Pogingen om overeen te komen met de algemene verwachtingen van routeringsgedrag.

Denk bijvoorbeeld aan sjablonen /Products/List en /Products/{id}. Het zou redelijk zijn om ervan uit te gaan dat /Products/List een betere overeenkomst is dan /Products/{id} voor het URL-pad /Products/List. Dit werkt omdat het letterlijke segment /List wordt beschouwd als een betere prioriteit dan het parametersegment /{id}.

De details van hoe prioriteit werkt, zijn gekoppeld aan de wijze waarop routesjablonen worden gedefinieerd:

  • Sjablonen met meer segmenten worden als specifieker beschouwd.
  • Een segment met letterlijke tekst wordt beschouwd als specifieker dan een parametersegment.
  • Een parametersegment met een beperking wordt beschouwd als specifieker dan één zonder.
  • Een complex segment wordt beschouwd als even specifiek als een parametersegment met een beperking.
  • Catch-all-parameters zijn het minst specifiek. Zie catch-all in de sectie Routesjablonen voor belangrijke informatie over catch-all routes.

Concepten voor het genereren van URL's

URL generation:

  • Is het proces waarmee routering een URL-pad kan maken op basis van een set routewaarden.
  • Hiermee is een logische scheiding mogelijk tussen eindpunten en de URL's die er toegang toe hebben.

Eindpuntroutering omvat de LinkGenerator API. LinkGenerator is een singleton-service die beschikbaar is via DI. De LinkGenerator API kan buiten de context van een uitvoeringsaanvraag worden gebruikt. Mvc.IUrlHelper en scenario's die afhankelijk zijn IUrlHelpervan, zoals Tag Helpers, HTML Helpers en Actieresultaten, gebruiken de LinkGenerator API intern om mogelijkheden voor het genereren van koppelingen te bieden.

De koppelingsgenerator wordt ondersteund door het concept van adres- en adresschema's. Een adresschema is een manier om de eindpunten te bepalen die moeten worden overwogen voor het genereren van koppelingen. De scenario's van routenaam en routewaarden die voor veel gebruikers bekend zijn, worden met controllers en Razor Pagina's geïmplementeerd als een adresschema.

De koppelingsgenerator kan via de volgende uitbreidingsmethoden een koppeling maken naar controllers en Razor pagina's:

Overbelastingen van deze methoden accepteren argumenten die de HttpContext. Deze methoden zijn functioneel gelijkwaardig aan Url.Action en Url.Page, maar bieden extra flexibiliteit en opties.

De GetPath* methoden zijn het meest vergelijkbaar met Url.Action en Url.Page, omdat ze een URI genereren die een absoluut pad bevat. De GetUri* methoden genereren altijd een absolute URI die een schema en host bevat. De methoden die een HttpContext accepteren, genereren een URI in de context van de lopende aanvraag. De omgevingsroutewaarden , het URL-basispad, het schema en de host van de uitvoerbare aanvraag worden gebruikt, tenzij deze worden overschreven.

LinkGenerator kan worden aangeroepen met een adres. Het genereren van een URI vindt plaats in twee stappen:

  1. Een adres is gebonden aan een lijst met eindpunten die overeenkomen met het adres.
  2. De eindpunten RoutePattern worden geëvalueerd totdat een routepatroon dat overeenkomt met de opgegeven waarden wordt gevonden. De resulterende uitvoer wordt gecombineerd met de andere URI-onderdelen die aan de koppelingsgenerator worden geleverd en geretourneerd.

De methoden die door LinkGenerator worden geboden, ondersteunen het genereren van standaardkoppelingen voor elk type adres. De handigste manier om de koppelingsgenerator te gebruiken, is via extensiemethoden die bewerkingen uitvoeren voor een specifiek adrestype:

Extension Method Description
GetPathByAddress Genereert een URI met een absoluut pad op basis van de opgegeven waarden.
GetUriByAddress Genereert een absolute URI op basis van de opgegeven waarden.

Warning

Let op de volgende gevolgen van het aanroepen LinkGenerator van methoden:

  • Gebruik GetUri* extensiemethoden met voorzichtigheid in een app-configuratie die de Host header van binnenkomende aanvragen niet valideert. Als de Host header van binnenkomende aanvragen niet wordt gevalideerd, kan niet-vertrouwde aanvraaginvoer worden teruggestuurd naar de client in URI's in een weergave of pagina. Het is raadzaam dat alle productie-apps hun server configureren om de Host header te valideren op basis van bekende geldige waarden.

  • Wees LinkGenerator voorzichtig met middleware in combinatie met Map of MapWhen. Map* wijzigt het basispad van de uitvoerende aanvraag, wat van invloed is op de uitvoer van linkgeneratie. Alle API's maken het mogelijk om LinkGenerator een basispad op te geven. Geef een leeg basispad op om het effect bij het Map* genereren van koppelingen ongedaan te maken.

Middleware example

In het volgende voorbeeld gebruikt een middleware de LinkGenerator API om een koppeling te maken naar een actiemethode met een lijst met winkelproducten. Het gebruik van de koppelingsgenerator door deze in een klasse te injecteren en aanroepen GenerateLink is beschikbaar voor elke klasse in een 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.");
    }
}

Route templates

Tokens binnen {} definiëren routeparameters die worden gebonden als de route overeenkomt. Er kunnen meer dan één routeparameter worden gedefinieerd in een routesegment, maar routeparameters moeten worden gescheiden door een letterlijke waarde. For example:

{controller=Home}{action=Index}

is geen geldige route, omdat er geen numerieke waarde tussen {controller} en {action} staat. Routeparameters moeten een naam hebben en kunnen aanvullende kenmerken hebben opgegeven.

Letterlijke tekst anders dan routeparameters (bijvoorbeeld {id}) en het padscheidingsteken / moet overeenkomen met de tekst in de URL. Tekstvergelijking is niet hoofdlettergevoelig en is gebaseerd op de gedecodeerde weergave van het pad van de URL. Als u een letterlijk routeparameterscheidingsteken { wilt vergelijken of }, escapet u het scheidingsteken door het teken te herhalen. Bijvoorbeeld {{ of }}.

Sterretje * of dubbel sterretje **:

  • Kan worden gebruikt als voorvoegsel voor een routeparameter om verbinding te maken met de rest van de URI.
  • Worden een catch-all-parameters genoemd. Bijvoorbeeld: blog/{**slug}
    • Komt overeen met elke URI die begint met blog/ en die een waarde bevat die erop volgt.
    • De waarde die volgt op blog/ wordt toegewezen aan de slug routewaarde.

Warning

Een catch-all--parameter kan verkeerde routes matchen door een fout in de routering. Apps die door deze fout worden beïnvloed, hebben de volgende kenmerken:

  • Een algemene route, bijvoorbeeld {**slug}"
  • De catch-all-route kan niet overeenkomen met aanvragen die moeten overeenkomen.
  • Als u andere routes verwijdert, begint de catch-all route te werken.

Zie GitHub-bugs 18677 en 16579 voor voorbeeldgevallen die deze bug ervaren.

Een opt-in-oplossing voor deze fout is opgenomen in .NET Core 3.1.301 of hoger SDK. Met de volgende code wordt een interne switch ingesteld waarmee deze fout wordt opgelost:

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

Catch-all-parameters kunnen ook overeenkomen met de lege tekenreeks.

De catch-all-parameter escapeert de juiste tekens wanneer de route wordt gebruikt om een URL te genereren, inclusief padscheidingstekens /. De route foo/{*path} met routewaarden { path = "my/path" } genereert foo/my%2Fpathbijvoorbeeld . Noteer de escaped slash. Gebruik het voorvoegsel van de ** routeparameter om het padscheidingsteken af te ronden. De route foo/{**path} met { path = "my/path" } genereert foo/my/path.

URL-patronen die proberen een bestandsnaam met een optionele bestandsextensie vast te leggen, hebben aanvullende overwegingen. Denk bijvoorbeeld aan de sjabloon files/{filename}.{ext?}. Wanneer waarden voor beide filename en ext bestaan, worden beide waarden ingevuld. Als er slechts een waarde voor filename bestaat in de URL, komt de route overeen omdat het volgtraject . optioneel is. De volgende URL's komen overeen met deze route:

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

Routeparameters kunnen standaardwaarden hebben die zijn aangewezen door de standaardwaarde op te geven na de parameternaam gescheiden door een gelijkteken (=). Definieert {controller=Home} bijvoorbeeld Home als de standaardwaarde voor controller. De standaardwaarde wordt gebruikt als er geen waarde aanwezig is in de URL voor de parameter. Routeparameters worden optioneel gemaakt door een vraagteken (?) toe te voegen aan het einde van de parameternaam. Bijvoorbeeld: id?. Het verschil tussen optionele waarden en standaardrouteparameters is:

  • Een routeparameter met een standaardwaarde produceert altijd een waarde.
  • Een optionele parameter heeft alleen een waarde wanneer een waarde wordt opgegeven door de aanvraag-URL.

Routeparameters kunnen beperkingen hebben die overeenkomen met de routewaarde die is gebonden aan de URL. Door : en de naam van de beperking na de naam van de routeparameter toe te voegen, wordt een inline beperking voor een routeparameter gespecificeerd. Als voor de beperking argumenten zijn vereist, staan deze tussen haakjes (...) achter de naam van de beperking. Er kunnen meerdere inlinebeperkingen worden opgegeven door een andere : naam en beperking toe te voegen.

De naam en argumenten van de beperking worden aan de IInlineConstraintResolver-service doorgegeven om een exemplaar van IRouteConstraint te maken, voor gebruik in URL-verwerking. De routesjabloon blog/{article:minlength(10)} geeft bijvoorbeeld een minlength beperking op met het argument 10. Zie de sectie Routebeperkingen voor meer informatie over routebeperkingen en een lijst met de beperkingen die door het framework worden geboden.

Routeparameters kunnen ook parametertransformatoren hebben. Parametertransformatoren transformeren de waarde van een parameter bij het genereren van koppelingen en overeenkomende acties en pagina's naar URL's. Net zoals beperkingen kunnen parametertransformatoren inline aan een routeparameter worden toegevoegd door na de naam van de routeparameter een : en de naam van de transformer toe te voegen. De routesjabloon blog/{article:slugify} geeft bijvoorbeeld een slugify transformator op. Zie de sectie Parametertransformatoren voor meer informatie over parametertransformaties .

In de volgende tabel ziet u voorbeeldroutesjablonen en hun gedrag:

Route Template Voorbeeld van overeenkomende URI De aanvraag-URI...
hello /hello Komt alleen overeen met het ene pad /hello.
{Page=Home} / Koppelt en stelt Page in op Home.
{Page=Home} /Contact Koppelt en stelt Page in op Contact.
{controller}/{action}/{id?} /Products/List Wordt toegewezen aan de Products controller en List actie.
{controller}/{action}/{id?} /Products/Details/123 Wordt toegewezen aan de controller Products en de actie Details, waarbij id is ingesteld op 123.
{controller=Home}/{action=Index}/{id?} / Verwijst naar de Home controller en Index methode. id wordt genegeerd.
{controller=Home}/{action=Index}/{id?} /Products Verwijst naar de Products controller en Index methode. id wordt genegeerd.

Het gebruik van een sjabloon is over het algemeen de eenvoudigste methode voor routering. Beperkingen en standaardwaarden kunnen ook buiten de routesjabloon worden opgegeven.

Complex segments

Complexe segmenten worden verwerkt door letterlijke scheidingstekens van rechts naar links op een niet-gretige wijze te vergelijken. Is bijvoorbeeld [Route("/a{b}c{d}")] een complex segment. Complexe segmenten werken op een bepaalde manier die moet worden begrepen om ze succesvol te kunnen gebruiken. In het voorbeeld in deze sectie ziet u waarom complexe segmenten alleen goed werken wanneer de tekst van het scheidingsteken niet binnen de parameterwaarden wordt weergegeven. Het gebruik van een regex en het handmatig extraheren van de waarden is nodig voor complexere gevallen.

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Dit is een samenvatting van de stappen die routering uitvoert met de sjabloon /a{b}c{d} en het URL-pad /abcd. De | functie wordt gebruikt om te visualiseren hoe het algoritme werkt:

  • De eerste letterlijke waarde, van rechts naar links, is c. Dus /abcd wordt vanaf rechts gezocht en vindt /ab|c|d.
  • Alles aan de rechterkant (d) komt nu overeen met de routeparameter {d}.
  • De volgende letterlijke waarde is a, gelezen van rechts naar links. Dus /ab|c|d wordt gezocht vanaf waar we gebleven waren, dan a wordt gevonden /|a|b|c|d.
  • De waarde aan de rechterkant (b) komt nu overeen met de routeparameter {b}.
  • Er is geen resterende tekst en geen resterende routesjabloon, dus dit komt overeen.

Hier volgt een voorbeeld van een negatief geval met dezelfde sjabloon /a{b}c{d} en het URL-pad /aabcd. De | functie wordt gebruikt om te visualiseren hoe het algoritme werkt. Dit geval is geen overeenkomst, wat wordt uitgelegd door hetzelfde algoritme:

  • De eerste letterlijke waarde, van rechts naar links, is c. Dus /aabcd wordt vanaf rechts gezocht en vindt /aab|c|d.
  • Alles aan de rechterkant (d) komt nu overeen met de routeparameter {d}.
  • De volgende letterlijke waarde is a, gelezen van rechts naar links. Dus /aab|c|d wordt gezocht vanaf waar we gebleven waren, dan a wordt gevonden /a|a|b|c|d.
  • De waarde aan de rechterkant (b) komt nu overeen met de routeparameter {b}.
  • Op dit moment is er nog tekst a, maar het algoritme heeft geen routesjabloon meer om te parseren, dus dit is geen overeenkomst.

Omdat het overeenkomende algoritme niet-graaiend is:

  • Deze komt overeen met de kleinste hoeveelheid tekst die in elke stap mogelijk is.
  • Elk geval waarin de scheidingstekenwaarde in de parameterwaarden wordt weergegeven, resulteert in niet-overeenkomende waarden.

Reguliere expressies bieden veel meer controle over hun overeenkomende gedrag.

Greedy matching, ook wel lazy matching genoemd, komt overeen met de grootst mogelijke tekenreeks. Niet-greedy komt overeen met de kleinste mogelijke tekenreeks.

Routeren met speciale tekens

Routering met speciale tekens kan leiden tot onverwachte resultaten. Denk bijvoorbeeld aan een controller met de volgende actiemethode:

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

Wanneer string id deze de volgende gecodeerde waarden bevat, kunnen onverwachte resultaten optreden:

ASCII Encoded
/ %2F
+

Routeparameters worden niet altijd gedecodeerd. Dit probleem kan in de toekomst worden opgelost. Zie dit GitHub-probleem voor meer informatie;

Route constraints

Routebeperkingen worden uitgevoerd wanneer er een overeenkomst is opgetreden met de binnenkomende URL en het URL-pad wordt omgezet in routewaarden. Routebeperkingen inspecteren doorgaans de routewaarde die via de routesjabloon is gekoppeld en bepalen of de waarde acceptabel is door een waar of onwaar beslissing te maken. Bij sommige routebeperkingen worden gegevens buiten de routewaarde gebruikt om na te gaan of de aanvraag kan worden gerouteerd. Bijvoorbeeld kan de HttpMethodRouteConstraint een aanvraag accepteren of weigeren op basis van het HTTP-werkwoord. Beperkingen worden gebruikt bij het genereren van routeringsaanvragen en het genereren van koppelingen.

Warning

Gebruik geen beperkingen voor invoervalidatie. Als beperkingen worden gebruikt voor invoervalidatie, resulteert ongeldige invoer in een 404 antwoord niet gevonden. Ongeldige invoer moet een 400 ongeldige aanvraag opleveren met een geschikt foutbericht. Routebeperkingen worden gebruikt om vergelijkbare routes te ontkoppelen, niet om de invoer voor een bepaalde route te valideren.

In de volgende tabel ziet u voorbeeld van routebeperkingen en hun verwachte gedrag:

constraint Example Example Matches Notes
int {id:int} 123456789, -123456789 Komt overeen met een geheel getal
bool {active:bool} true, FALSE Komt overeen met true of false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Komt overeen met een geldige DateTime waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
decimal {price:decimal} 49.99, -1,000.01 Komt overeen met een geldige decimal waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
double {weight:double} 1.234, -1,001.01e8 Komt overeen met een geldige double waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
float {weight:float} 1.234, -1,001.01e8 Komt overeen met een geldige float waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Komt overeen met een geldige Guid waarde
long {ticks:long} 123456789, -123456789 Komt overeen met een geldige long waarde
minlength(value) {username:minlength(4)} Rick Tekenreeks moet ten minste 4 tekens bevatten
maxlength(value) {filename:maxlength(8)} MyFile Tekenreeks mag niet langer zijn dan 8 tekens
length(length) {filename:length(12)} somefile.txt String moet exact 12 tekens lang zijn
length(min,max) {filename:length(8,16)} somefile.txt Een reeks moet minimaal 8 en niet meer dan 16 tekens lang zijn
min(value) {age:min(18)} 19 De waarde van het gehele getal moet ten minste 18 zijn
max(value) {age:max(120)} 91 Een geheel getal mag niet meer dan 120 zijn
range(min,max) {age:range(18,120)} 91 Een geheel getal moet ten minste 18 zijn, maar niet meer dan 120
alpha {name:alpha} Rick Een tekenreeks moet uit één of meer alfabetische tekens a-z bestaan en is hoofdletterongevoelig.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 De tekenreeks moet overeenkomen met de reguliere expressie. Zie tips voor het definiëren van een reguliere expressie.
required {name:required} Rick Wordt gebruikt om af te dwingen dat er een niet-parameterwaarde aanwezig is tijdens het genereren van de URL

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Meerdere, door dubbele punt gescheiden beperkingen kunnen worden toegepast op één parameter. Met de volgende beperking wordt bijvoorbeeld een parameter beperkt tot een geheel getal van 1 of hoger:

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

Warning

Routebeperkingen die de URL controleren en worden geconverteerd naar een CLR-type, gebruiken altijd de invariante cultuur. Bijvoorbeeld conversie naar het CLR-type int of DateTime. Bij deze beperkingen wordt ervan uitgegaan dat de URL niet kan worden gelokaliseerd. De door het framework geleverde routebeperkingen wijzigen niet de waarden die zijn opgeslagen in routewaarden. Alle routewaarden die uit de URL worden geparseerd, worden opgeslagen als tekenreeksen. De beperking probeert bijvoorbeeld float de routewaarde te converteren naar een float, maar de geconverteerde waarde wordt alleen gebruikt om te controleren of deze kan worden geconverteerd naar een float.

Reguliere expressies in beperkingen

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Reguliere expressies kunnen worden opgegeven als inlinebeperkingen met behulp van de regex(...) routebeperking. Methoden in de MapControllerRoute familie accepteren ook een object-literal met beperkingen. Als dit formulier wordt gebruikt, worden tekenreekswaarden geïnterpreteerd als reguliere expressies.

De volgende code maakt gebruik van een inline regex-beperking:

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

In de volgende code wordt een letterlijk object gebruikt om een regex-beperking op te geven:

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

Het ASP.NET Core-framework voegt RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant toe aan de reguliere expressie-constructor. Zie RegexOptions voor een beschrijving van deze leden.

Reguliere expressies maken gebruik van scheidingstekens en tokens die vergelijkbaar zijn met tokens die worden gebruikt door routering en de C#-taal. Tokens voor reguliere expressies moeten worden geëscaped. Als u de reguliere expressie ^\d{3}-\d{2}-\d{4}$ in een inlinebeperking wilt gebruiken, gebruikt u een van de volgende opties:

  • Vervang \ tekens in de tekenreeks als \\ tekens in het C#-bronbestand om het escapeteken voor tekenreeksen \ te escapen.
  • Verbatim tekenreeksen.

Als u tekens voor scheidingstekens van routeringsparameters wilt escapen, verdubbel dan de tekens in de expressie, bijvoorbeeld {, }, [, ], {{, }}, [[, ]]. In de volgende tabel ziet u een reguliere expressie en de escape-versie:

Regular expression Geëscapede reguliere expressie
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Reguliere expressies die in routering worden gebruikt, beginnen vaak met het ^ teken en komen overeen met de beginpositie van de tekenreeks. De expressies eindigen vaak met het $ teken en komen overeen met het einde van de tekenreeks. De ^ en $ tekens zorgen ervoor dat de reguliere expressie overeenkomt met de volledige waarde van de routeparameter. Zonder de ^ en $ tekens komt de reguliere expressie overeen met een subtekenreeks binnen de tekenreeks, wat vaak ongewenst is. De volgende tabel bevat voorbeelden en legt uit waarom ze overeenkomen of niet overeenkomen:

Expression String Match Comment
[a-z]{2} hello Yes Substring matches
[a-z]{2} 123abc456 Yes Substring matches
[a-z]{2} mz Yes Matches expression
[a-z]{2} MZ Yes Niet hoofdlettergevoelig
^[a-z]{2}$ hello No Zie ^ en $ hierboven
^[a-z]{2}$ 123abc456 No Zie ^ en $ hierboven

Zie .NET Framework Regular Expressions voor meer informatie over de syntaxis van reguliere expressies.

Als u een parameter wilt beperken tot een bekende set mogelijke waarden, gebruikt u een reguliere expressie. Komt bijvoorbeeld {action:regex(^(list|get|create)$)} alleen overeen met de action routewaarde naar list, getof create. Wanneer dit wordt doorgegeven aan een woordenlijst met beperkingen, is de tekenreeks ^(list|get|create)$ gelijk. Beperkingen die worden doorgegeven in de woordenlijst met beperkingen die niet overeenkomen met een van de bekende beperkingen, worden ook beschouwd als reguliere expressies. Beperkingen die worden doorgegeven binnen een sjabloon die niet overeenkomen met een van de bekende beperkingen, worden niet behandeld als reguliere expressies.

Aangepaste routebeperkingen

Aangepaste routebeperkingen kunnen worden gemaakt door de IRouteConstraint interface te implementeren. De IRouteConstraint interface bevat Match, die retourneert true als aan de beperking wordt voldaan en false anders.

Aangepaste routebeperkingen zijn zelden nodig. Voordat u een aangepaste routebeperking implementeert, moet u alternatieven overwegen, zoals modelbinding.

De map ASP.NET Kernbeperkingen biedt goede voorbeelden van het maken van beperkingen. Bijvoorbeeld GuidRouteConstraint.

Als u een aangepast type IRouteConstraintroutebeperking wilt gebruiken, moet het type routebeperking worden geregistreerd bij de app ConstraintMap in de servicecontainer. Een ConstraintMap is een woordenlijst die routebeperkingssleutels toe wijst aan IRouteConstraint implementaties die deze beperkingen valideren. ConstraintMap van een app kan in Program.cs worden bijgewerkt als onderdeel van een AddRouting-oproep of door RouteOptions rechtstreeks met builder.Services.Configure<RouteOptions> te configureren. For example:

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

De voorgaande beperking wordt toegepast in de volgende code:

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

De implementatie van NoZeroesRouteConstraint voorkomt dat 0 in een routeparameter wordt gebruikt:

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

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

De voorgaande code:

  • Voorkomt 0 in het {id} segment van de route.
  • Wordt weergegeven om een eenvoudig voorbeeld te geven van het implementeren van een aangepaste beperking. Deze mag niet worden gebruikt in een productie-app.

De volgende code is een betere benadering om te voorkomen dat een id met een 0 wordt verwerkt.

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

    return Content(id);
}

De voorgaande code heeft de volgende voordelen ten opzichte van de NoZeroesRouteConstraint aanpak:

  • Er is geen aangepaste beperking vereist.
  • Het retourneert een meer beschrijvende fout wanneer de routeparameter 0 bevat.

Parameter transformers

Parameter transformers:

Een aangepaste slugify parametertransformator in routepatroon blog\{article:slugify} met Url.Action(new { article = "MyTestArticle" }) genereert blog\my-test-articlebijvoorbeeld .

Houd rekening met de volgende IOutboundParameterTransformer implementatie:

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

Als u een parametertransformatie in een routepatroon wilt gebruiken, configureert u deze met behulp van ConstraintMapProgram.cs:

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

Het ASP.NET Core-framework gebruikt parametertransformatoren om de URI te transformeren waarop een eindpunt wordt afgerond. Met parametertransformatoren worden bijvoorbeeld de routewaarden getransformeerd die worden gebruikt om overeen te komen met een area, controller, action, en page.

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

Met de voorgaande routesjabloon wordt de actie SubscriptionManagementController.GetAll vergeleken met de URI /subscription-management/get-all. Een parametertransformator wijzigt niet de routewaarden die worden gebruikt om een koppeling te genereren. Bijvoorbeeld Url.Action("GetAll", "SubscriptionManagement") produceert /subscription-management/get-all.

ASP.NET Core biedt API-conventies voor het gebruik van parametertransformatoren met gegenereerde routes:

Naslaginformatie over het genereren van URL's

Deze sectie bevat een verwijzing voor het algoritme dat is geïmplementeerd door het genereren van url's. In de praktijk gebruiken de meeste complexe voorbeelden van het genereren van URL's controllers of Razor pagina's. Zie routering in controllers voor meer informatie.

Het proces voor het genereren van URL's begint met een aanroep naar LinkGenerator.GetPathByAddress of een vergelijkbare methode. De methode wordt geleverd met een adres, een set routewaarden en eventueel informatie over de huidige aanvraag van HttpContext.

De eerste stap is het gebruik van het adres om een set kandidaat-eindpunten op te lossen met behulp van een IEndpointAddressScheme<TAddress> eindpunt dat overeenkomt met het type adres.

Zodra de set kandidaten door het adresschema is gevonden, worden de eindpunten iteratief gerangschikt en verwerkt totdat een URL-generatiebewerking is geslaagd. Het genereren van url's controleert niet op dubbelzinnigheden. Het eerste resultaat dat wordt geretourneerd, is het uiteindelijke resultaat.

Problemen bij het genereren van URL's met logboekregistratie oplossen

De eerste stap bij het oplossen van problemen met URL-generatie is het instellen van het niveau van de logboekregistratie op Microsoft.AspNetCore.RoutingTRACE. LinkGenerator registreert veel details over de verwerking die nuttig kan zijn om problemen op te lossen.

Zie naslaginformatie over het genereren van URL's voor meer informatie over het genereren van url's.

Addresses

Adressen zijn het concept in het genereren van URL's dat wordt gebruikt om een aanroep aan de koppelingsgenerator te binden aan een set kandidaat-eindpunten.

Adressen zijn een uitbreidbaar concept dat standaard wordt geleverd met twee implementaties:

  • Gebruik de eindpuntnaam (string) als het adres:
    • Biedt vergelijkbare functionaliteit als de routenaam van MVC.
    • Maakt gebruik van het IEndpointNameMetadata metagegevenstype.
    • Hiermee wordt de opgegeven reeks afgestemd op de metagegevens van alle geregistreerde eindpunten.
    • Genereert een uitzondering bij het opstarten als meerdere eindpunten dezelfde naam gebruiken.
    • Aanbevolen voor algemeen gebruik buiten controllers en Razor pagina's.
  • Routewaarden (RouteValuesAddress) gebruiken als het adres:
    • Biedt vergelijkbare functionaliteit voor controllers en Razor pagina's voor het genereren van verouderde URL's.
    • Zeer complex om uit te breiden en fouten op te sporen.
    • Biedt de implementatie die wordt gebruikt door IUrlHelper, Tag Helpers, HTML Helpers, Actieresultaten, enzovoort.

De rol van het adresschema is het maken van de koppeling tussen het adres en overeenkomende eindpunten op willekeurige criteria:

  • Het eindpuntnaamschema voert een eenvoudige woordenboekopzoeking uit.
  • Het schema voor routewaarden heeft een complex algoritme voor het bepalen van de beste subset van een set.

Omgevingswaarden en expliciete waarden

Vanuit de huidige aanvraag heeft routering toegang tot de routewaarden van de huidige aanvraag HttpContext.Request.RouteValues. De waarden die aan de huidige aanvraag zijn gekoppeld, worden de omgevingswaarden genoemd. Ter duidelijkheid verwijst de documentatie naar de routewaarden die worden doorgegeven aan methoden als expliciete waarden.

In het volgende voorbeeld ziet u omgevingswaarden en expliciete waarden. Het biedt omgevingswaarden van de huidige aanvraag en expliciete waarden:

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

    // ...

De voorgaande code:

De volgende code bevat alleen expliciete waarden en geen omgevingswaarden:

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

De voorgaande methode retourneert /Home/Subscribe/17

De volgende code in de WidgetController retourneert /Widget/Subscribe/17:

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

De volgende code biedt de controller vanuit omgevingsvariabelen in het huidige verzoek en expliciete waarden:

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

In de voorgaande code:

  • /Gadget/Edit/17 wordt geretourneerd.
  • Url haalt de IUrlHelper.
  • Action genereert een URL met een absoluut pad voor een actiemethode. De URL bevat de opgegeven action naam en route waarden.

De volgende code bevat omgevingswaarden van de huidige aanvraag en expliciete waarden:

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

        // ...
    }
}

De voorgaande code stelt url in op /Edit/17 wanneer de pagina Bewerken Razor de volgende pagina-instructie bevat:

@page "{id:int}"

Als de pagina Bewerken de "{id:int}" routesjabloon niet bevat, is url/Edit?id=17.

Het gedrag van MVC's IUrlHelper voegt een complexiteitslaag toe, naast de regels die hier worden beschreven:

  • IUrlHelper levert altijd de routewaarden van de huidige aanvraag als omgevingswaarden.
  • IUrlHelper.Action kopieert altijd de huidige action en controller routewaarden als expliciete waarden, tenzij deze worden overschreven door de ontwikkelaar.
  • IUrlHelper.Page kopieert de huidige page routewaarde altijd als een expliciete waarde, tenzij deze wordt overschreven.
  • IUrlHelper.Page overschrijft altijd de huidige handler routewaarde met null als expliciete waarde, tenzij null wordt overschreven.

Gebruikers worden vaak verrast door de gedragsdetails van omgevingswaarden, omdat MVC geen eigen regels lijkt te volgen. Om historische en compatibiliteitsredenen hebben bepaalde routewaarden zoals action, controller, page, en handler hun eigen speciale gedrag.

De equivalente functionaliteit die wordt geleverd door LinkGenerator.GetPathByAction en LinkGenerator.GetPathByPage dupliceert deze afwijkingen van IUrlHelper voor compatibiliteit.

Proces voor het genereren van URL's

Zodra de set kandidaat-eindpunten is gevonden, genereert het algoritme URL's.

  • Hiermee worden de eindpunten iteratief verwerkt.
  • Retourneert het eerste geslaagde resultaat.

De eerste stap in dit proces wordt routewaardevervalsing genoemd. Routewaarde-invalidering is het proces waarmee routering bepaalt welke routewaarden van de ambiente waarden moeten worden gebruikt en welke moeten worden genegeerd. Elke omgevingswaarde wordt beschouwd en gecombineerd met de expliciete waarden of genegeerd.

De beste manier om na te denken over de rol van omgevingswaarden is dat ze proberen toepassingsontwikkelaars te helpen minder te typen in veelvoorkomende gevallen. Normaal gesproken zijn de scenario's waarin omgevingswaarden nuttig zijn gerelateerd aan MVC:

  • Wanneer u een koppeling maakt naar een andere actie in dezelfde controller, hoeft de controllernaam niet te worden opgegeven.
  • Wanneer u een koppeling maakt naar een andere controller in hetzelfde gebied, hoeft de gebiedsnaam niet te worden opgegeven.
  • Bij het koppelen aan dezelfde actiemethode hoeven routewaarden niet te worden opgegeven.
  • Wanneer u een koppeling maakt naar een ander deel van de app, wilt u geen routewaarden overdragen die geen betekenis hebben in dat deel van de app.

Aanroepen naar LinkGenerator of IUrlHelper die terug null geven, worden meestal veroorzaakt door het niet begrijpen van de invalidering van de routewaarde. Los ongeldige routewaarden op door expliciet meer routewaarden op te geven om te zien of het probleem hierdoor wordt opgelost.

Invalidatie van routewaarden werkt volgens de veronderstelling dat het URL-schema van de app hiërarchisch is, met een hiërarchie die van links naar rechts is gevormd. Overweeg de basissjabloon {controller}/{action}/{id?} voor controllerroute om een intuïtief beeld te krijgen van hoe dit in de praktijk werkt. Als u een waarde wijzigt , worden alle routewaarden die rechts worden weergegeven, ongeldig . Dit weerspiegelt de aanname over hiërarchie. Als de app een omgevingswaarde heeft voor id, en de bewerking een andere waarde specificeert voor de controller:

  • id wordt niet hergebruikt omdat {controller} links van {id?} staat.

Enkele voorbeelden waarin dit principe wordt gedemonstreerd:

  • Als de expliciete waarden een waarde bevatten voor id, wordt de omgevingswaarde genegeerd id . De omgevingswaarden voor controller en action kunnen worden gebruikt.
  • Als de expliciete waarden een waarde bevatten voor action, wordt een omgevingswaarde action voor genegeerd. De omgevingswaarden voor controller kunnen worden gebruikt. Als de expliciete waarde action anders is dan de omgevingswaarde, actionwordt de id waarde niet gebruikt. Als de expliciete waarde voor action hetzelfde is als de omgevingswaarde voor action, kan de id waarde worden gebruikt.
  • Als de expliciete waarden een waarde bevatten voor controller, wordt een omgevingswaarde controller voor genegeerd. Als de expliciete waarde controller verschilt van de omgevingswaarde voor controller, worden de action waarden id niet gebruikt. Als de expliciete waarde voor controller dezelfde is als de omgevingswaarde voor controller, kunnen de waarden action en id worden gebruikt.

Dit proces wordt verder gecompliceerd door het bestaan van kenmerkroutes en toegewezen conventionele routes. Conventionele routes, zoals {controller}/{action}/{id?}, geven een hiërarchie aan met behulp van routeparameters. Voor specifieke conventionele routes en attributieroutes naar controllers en Razor pagina's:

  • Er is een hiërarchie met routewaarden.
  • Ze worden niet weergegeven in de sjabloon.

In deze gevallen definieert het genereren van URL's het concept van de vereiste waarden . Eindpunten die zijn gemaakt door controllers en Razor pagina's hebben vereiste waarden opgegeven waarmee routewaardevervalidering kan worden uitgevoerd.

Het invalidatie-algoritme voor de routewaarde in detail:

  • De vereiste waardenamen worden gecombineerd met de routeparameters en worden vervolgens van links naar rechts verwerkt.
  • Voor elke parameter worden de omgevingswaarde en expliciete waarde vergeleken:
    • Als de omgevingswaarde en expliciete waarde hetzelfde zijn, wordt het proces voortgezet.
    • Als de omgevingswaarde aanwezig is en de expliciete waarde niet is, wordt de omgevingswaarde gebruikt bij het genereren van de URL.
    • Als de omgevingswaarde niet aanwezig is en de expliciete waarde is, negeert u de omgevingswaarde en alle volgende omgevingswaarden.
    • Als de omgevingswaarde en de expliciete waarde aanwezig zijn en de twee waarden verschillen, negeert u de omgevingswaarde en alle volgende omgevingswaarden.

Op dit moment is de bewerking voor het genereren van URL's gereed om routebeperkingen te evalueren. De set van geaccepteerde waarden wordt samen met de standaardwaarden van de parameter verstrekt aan de beperkingen. Als aan alle beperkingen is voldaan, wordt de bewerking voortgezet.

Vervolgens kunnen de geaccepteerde waarden worden gebruikt om de routesjabloon uit te vouwen. De routesjabloon wordt verwerkt:

  • From left-to-right.
  • Elke parameter wordt vervangen door de geaccepteerde waarde.
  • Met de volgende speciale gevallen:
    • Als er een waarde ontbreekt in de geaccepteerde waarden en de parameter een standaardwaarde heeft, wordt de standaardwaarde gebruikt.
    • Als er een waarde ontbreekt in de geaccepteerde waarden en de parameter optioneel is, wordt de verwerking voortgezet.
    • Als een routeparameter rechts van een ontbrekende optionele parameter een waarde heeft, mislukt de bewerking.
    • Aaneengesloten parameters met standaardwaarden en optionele parameters worden indien mogelijk samengevouwen.

Waarden die expliciet zijn opgegeven die niet overeenkomen met een segment van de route, worden toegevoegd aan de querytekenreeks. In de volgende tabel ziet u het resultaat wanneer u de routesjabloon {controller}/{action}/{id?}gebruikt.

Ambient Values Explicit Values Result
controller = "Home" action = "Info" /Home/About
controller = "Home" controller = "Order", action = "About" /Order/About
controller = "Home", color = "Rood" action = "Info" /Home/About
controller = "Home" action = "About", color = "Red" /Home/About?color=Red

Problemen met het ongeldig maken van routewaarde

De volgende code toont een voorbeeld van een URL-generatieschema dat niet wordt ondersteund door routering:

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

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

In de voorgaande code wordt de culture routeparameter gebruikt voor lokalisatie. De wens is om de culture parameter altijd als omgevingswaarde te laten accepteren. De culture parameter wordt echter niet geaccepteerd als een omgevingswaarde vanwege de manier waarop vereiste waarden werken:

  • In de "default" routesjabloon bevindt de culture routeparameter zich links van controller, zodat wijzigingen aan controllerculture niet ongeldig maken.
  • Binnen de "blog" routesjabloon wordt de culture routeparameter beschouwd als rechts van controller, die wordt weergegeven in de vereiste waarden.

URL-paden parseren met LinkParser

De LinkParser klasse voegt ondersteuning toe voor het parseren van een URL-pad in een set routewaarden. De ParsePathByEndpointName methode gebruikt een eindpuntnaam en een URL-pad en retourneert een set routewaarden die zijn geëxtraheerd uit het URL-pad.

In de volgende voorbeeldcontroller gebruikt de GetProduct actie een routesjabloon van api/Products/{id} en heeft een Name van GetProduct:

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

In dezelfde controllerklasse verwacht de AddRelatedProduct actie een URL-pad, pathToRelatedProductdat kan worden opgegeven als een queryreeksparameter:

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

    // ...

In het voorgaande voorbeeld haalt de AddRelatedProduct actie de id routewaarde op uit het URL-pad. Met een URL-pad van /api/Products/1bijvoorbeeld, wordt de relatedProductId waarde ingesteld op 1. Met deze benadering kunnen de CLIENTS van de API URL-paden gebruiken bij het verwijzen naar resources, zonder dat u hoeft te weten hoe een DERGELIJKE URL is gestructureerd.

Eindpuntmetagegevens configureren

De volgende koppelingen bevatten informatie over het configureren van eindpuntmetagegevens:

Hostkoppeling in routes met RequireHost

RequireHost past een beperking toe op de route waarvoor de opgegeven host is vereist. De RequireHost-parameter of de [Host] kan een:

  • Host: www.domain.com, komt www.domain.com overeen met elke poort.
  • Host met wildcard: *.domain.com, komt overeen met www.domain.com, subdomain.domain.com, of www.subdomain.domain.com op een willekeurige poort.
  • Poort: *:5000, komt overeen met poort 5000 met elke host.
  • Host en poort: www.domain.com:5000 of *.domain.com:5000, komt overeen met host en poort.

Er kunnen meerdere parameters worden opgegeven met of RequireHost[Host]. De beperking komt overeen met hosts die geldig zijn voor een van de parameters. Bijvoorbeeld, [Host("domain.com", "*.domain.com")] komt overeen met domain.com, www.domain.com, en subdomain.domain.com.

De volgende code gebruikt RequireHost om de opgegeven host op de route te vereisen:

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

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

De volgende code gebruikt het [Host] kenmerk op de controller om een van de opgegeven hosts te vereisen:

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

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

Wanneer het [Host] kenmerk wordt toegepast op zowel de controller als de actiemethode:

  • Het kenmerk van de actie wordt gebruikt.
  • Het controllerkenmerk wordt genegeerd.

Richtlijnen voor prestaties voor routering

Wanneer een app prestatieproblemen heeft, wordt routering vaak vermoed als het probleem. De reden waarom routering wordt vermoed, is dat frameworks zoals controllers en Razor Pagina's de hoeveelheid tijd rapporteren die binnen het framework is besteed in hun logboekberichten. Wanneer er een aanzienlijk verschil is tussen de tijd die door controllers wordt gerapporteerd en de totale tijd van de aanvraag:

  • Ontwikkelaars elimineren hun app-code als de bron van het probleem.
  • Het is gebruikelijk om aan te nemen dat routering de oorzaak is.

Routering wordt getest met behulp van duizenden eindpunten. Het is onwaarschijnlijk dat een typische app een prestatieprobleem ondervindt door te groot te zijn. De meest voorkomende hoofdoorzaak van trage routeringsprestaties is meestal een slecht gedragende aangepaste middleware.

In dit volgende codevoorbeeld ziet u een basistechniek voor het beperken van de bron van vertraging:

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

Voor tijdsplanning van routering:

  • Interleave elke middleware met een kopie van de timing-middleware die in de code hierboven wordt weergegeven.
  • Voeg een unieke id toe om de timinggegevens te correleren met de code.

Dit is een eenvoudige manier om de vertraging te beperken wanneer deze aanzienlijk is, bijvoorbeeld meer dan 10ms. Het aftrekken van Time 2 van Time 1 geeft de tijd weer die binnen de UseRouting middleware is besteed.

De volgende code maakt gebruik van een compactere benadering van de voorgaande tijdscode:

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

Mogelijk dure routeringsfuncties

De volgende lijst biedt inzicht in routeringsfuncties die relatief duur zijn vergeleken met basisroutesjablonen:

  • Reguliere expressies: het is mogelijk om reguliere expressies te schrijven die complex zijn of langlopende tijd hebben met een kleine hoeveelheid invoer.
  • Complexe segmenten ({x}-{y}-{z}):
    • Zijn aanzienlijk duurder dan het parseren van een normaal URL-padsegment.
    • Resulteert in veel meer subtekenreeksen die toegewezen worden.
  • Synchrone gegevenstoegang: veel complexe apps hebben databasetoegang als onderdeel van hun routering. Gebruik uitbreidbaarheidspunten zoals MatcherPolicy en EndpointSelectorContext, die asynchroon zijn.

Richtlijnen voor grote routetabellen

Standaard gebruikt ASP.NET Core een routeringsalgoritmen die geheugen ruilt voor CPU-tijd. Dit heeft het mooie effect dat routekoppelingstijd alleen afhankelijk is van de lengte van het pad dat moet worden vergeleken en niet het aantal routes. Deze benadering kan echter in sommige gevallen problematisch zijn, wanneer de app een groot aantal routes (in duizenden) heeft en er een grote hoeveelheid variabele voorvoegsels in de routes is. Als de routes bijvoorbeeld parameters hebben in vroege segmenten van de route, zoals {parameter}/some/literal.

Het is onwaarschijnlijk dat een app een situatie ondervindt waarbij dit een probleem is, tenzij:

  • Er is een groot aantal routes in de app met behulp van dit patroon.
  • Er is een groot aantal routes in de app.

Bepalen of een app het probleem met de grote routetabel ondervindt

  • Er zijn twee symptomen om naar te zoeken:
    • De app wordt langzaam gestart bij de eerste aanvraag.
      • Houd er rekening mee dat dit vereist is, maar niet voldoende. Er zijn veel andere niet-routeproblemen die kunnen leiden tot tragere opstart van apps. Controleer de onderstaande voorwaarde om nauwkeurig te bepalen of de app in deze situatie terechtkomt.
    • De app verbruikt veel geheugen tijdens het opstarten en een geheugendump toont een groot aantal Microsoft.AspNetCore.Routing.Matching.DfaNode exemplaren.

Dit probleem oplossen

Er zijn verschillende technieken en optimalisaties die kunnen worden toegepast op routes die dit scenario grotendeels verbeteren:

  • Pas waar mogelijk routebeperkingen toe op uw parameters, bijvoorbeeld {parameter:int}{parameter:guid}, {parameter:regex(\\d+)}enzovoort.
    • Hierdoor kan het routeringsalgoritme intern de structuren optimaliseren die worden gebruikt voor het vergelijken en drastisch verminderen van het gebruikte geheugen.
    • In de overgrote meerderheid van de gevallen volstaat dit om terug te keren naar een aanvaardbaar gedrag.
  • Wijzig de routes om parameters naar latere segmenten in de sjabloon te verplaatsen.
    • Het vermindert het aantal mogelijke paden dat overeenkomt met een eindpunt, gegeven een bepaald pad.
  • Gebruik een dynamische route en voer de koppeling dynamisch uit naar een controller/pagina.
    • Dit kan worden bereikt met behulp van MapDynamicControllerRoute en MapDynamicPageRoute.

Richtlijnen voor bibliotheekauteurs

Deze sectie bevat richtlijnen voor auteurs van bibliotheken die voortbouwen op routering. Deze details zijn bedoeld om ervoor te zorgen dat app-ontwikkelaars een goede ervaring hebben met het gebruik van bibliotheken en frameworks waarmee routering wordt uitgebreid.

Define endpoints

Als u een framework wilt maken dat gebruikmaakt van routing voor URL-koppeling, begint u met het definiëren van een gebruikerservaring die voortbouwt op UseEndpoints.

DO bovenop IEndpointRouteBuilder bouwen. Hierdoor kunnen gebruikers uw framework opstellen met andere ASP.NET Core-functies zonder verwarring. Elke ASP.NET Core-sjabloon bevat routering. Stel dat routering aanwezig is en bekend is voor gebruikers.

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

app.MapHealthChecks("/healthz");

DO retourneert een verzegeld betontype van een aanroep naar MapMyFramework(...) dat implementeert IEndpointConventionBuilder. De meeste frameworkmethoden Map... volgen dit patroon. De IEndpointConventionBuilder interface:

  • Hiermee kunnen metagegevens worden samengesteld.
  • Is gericht op verschillende uitbreidingsmethoden.

Door uw eigen type te declareren, kunt u uw eigen frameworkspecifieke functionaliteit toevoegen aan de opbouwfunctie. Het is prima om een door het framework gedeclareerde bouwer in te pakken en oproepen door te sturen.

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

app.MapHealthChecks("/healthz");

OVERWEEG om je eigen EndpointDataSourcete schrijven. EndpointDataSource is het primitieve laag-niveau mechanisme voor het declareren en bijwerken van een verzameling eindpunten. EndpointDataSource is een krachtige API die wordt gebruikt door controllers en Razor Pages.

De routeringstests hebben een eenvoudig voorbeeld van een gegevensbron die niet wordt bijgewerkt.

PROBEER NIET standaard een EndpointDataSource registratie uit te voeren. Vereisen dat gebruikers uw framework registreren in UseEndpoints. De filosofie van routering is dat er standaard niets is inbegrepen en dat UseEndpoints is de plek om eindpunten te registreren.

Routerings-geïntegreerde middleware maken

OVERWEEG metagegevenstypen te definiëren als een interface.

DO maakt het mogelijk om metagegevenstypen te gebruiken als een kenmerk voor klassen en methoden.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

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

Frameworks zoals controllers en Razor pagina's ondersteunen het toepassen van metagegevenskenmerken op typen en methoden. Als u metagegevenstypen declareert:

  • Maak ze toegankelijk als kenmerken.
  • De meeste gebruikers zijn bekend met het toepassen van kenmerken.

Door een metagegevenstype als interface te declareren, voegt u een andere flexibiliteitslaag toe:

  • Interfaces zijn samenstelbaar.
  • Ontwikkelaars kunnen hun eigen typen declareren die meerdere beleidsregels combineren.

DO maakt het mogelijk om metagegevens te overschrijven, zoals wordt weergegeven in het volgende voorbeeld:

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

De beste manier om deze richtlijnen te volgen, is om te voorkomen dat metagegevens van markeringen worden gedefinieerd:

  • Zoek niet alleen naar de aanwezigheid van een metagegevenstype.
  • Definieer een eigenschap op de metagegevens en controleer de eigenschap.

De verzameling metagegevens wordt gerangschikt en ondersteunt het overschrijven op prioriteit. In het geval van controllers zijn metagegevens voor de actiemethode het meest specifiek.

Maak middleware nuttig met en zonder routering:

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

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

Bekijk als voorbeeld van deze richtlijn de UseAuthorization middleware. Met de middleware voor autorisatie kunt u een terugvalbeleid doorgeven. Het terugvalbeleid, indien opgegeven, is van toepassing op beide:

  • Eindpunten zonder een opgegeven beleid.
  • Aanvragen die niet overeenkomen met een eindpunt.

Dit maakt de autorisatie-middleware nuttig buiten de context van routering. De autorisatie-middleware kan worden gebruikt voor traditionele middlewareprogrammering.

Debug diagnostics

Voor gedetailleerde routeringsdiagnose-uitvoer, stel Logging:LogLevel:Microsoft in op Debug. Stel in de ontwikkelomgeving het logboekniveau in:appsettings.Development.json

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

Additional resources

Routering is verantwoordelijk voor het koppelen van binnenkomende HTTP-aanvragen en het verzenden van deze aanvragen naar de uitvoerbare eindpunten van de app. Eindpunten zijn de eenheden van uitvoerbare aanvraagafhandelingscode van de app. Eindpunten worden gedefinieerd in de app en geconfigureerd wanneer de app wordt gestart. Het overeenkomende eindpuntproces kan waarden extraheren uit de URL van de aanvraag en deze waarden opgeven voor het verwerken van aanvragen. Met behulp van eindpuntgegevens van de app kan routering ook URL's genereren die zijn toegewezen aan eindpunten.

Apps kunnen routering configureren met behulp van:

In dit document worden details van ASP.NET Core-routering op laag niveau behandeld. Voor informatie over het configureren van routering:

Het eindpuntrouteringssysteem dat in dit document wordt beschreven, is van toepassing op ASP.NET Core 3.0 of hoger. Voor informatie over het vorige routeringssysteem op IRouterbasis van, selecteert u de ASP.NET Core 2.1-versie met behulp van een van de volgende methoden:

Voorbeeldcode bekijken of downloaden (hoe download je)

De downloadvoorbeelden voor dit document zijn ingeschakeld door een specifieke Startup klasse. Als u een specifiek voorbeeld wilt uitvoeren, moet u Program.cs aanpassen om de gewenste Startup-klasse aan te roepen.

Routing basics

Alle ASP.NET Core-sjablonen bevatten routering in de gegenereerde code. Routering wordt geregistreerd in de middleware-pijplijn in Startup.Configure.

De volgende code toont een basisvoorbeeld van routering:

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

Routering maakt gebruik van een paar middleware, geregistreerd door UseRouting en UseEndpoints:

  • UseRouting voegt routekoppeling toe aan de middleware-pijplijn. Deze middleware bekijkt de set eindpunten die in de app zijn gedefinieerd en selecteert de beste overeenkomst op basis van de aanvraag.
  • UseEndpoints voegt eindpuntuitvoering toe aan de middleware-pijplijn. Het voert de delegate uit die is gekoppeld aan het geselecteerde eindpunt.

Het voorgaande voorbeeld bevat één route naar het code-eindpunt met behulp van de MapGet-methode :

  • Wanneer een HTTP-aanvraag GET naar de hoofd-URL /wordt verzonden:
    • De weergegeven aanvraagdelegaat wordt uitgevoerd.
    • Hello World! wordt naar het HTTP-antwoord geschreven. De basis-URL / is standaard https://localhost:5001/.
  • Als de aanvraagmethode niet GET is of de hoofd-URL niet /is, worden er geen routeovereenkomsten en wordt een HTTP 404 geretourneerd.

Endpoint

De MapGet methode wordt gebruikt om een eindpunt te definiëren. Een eindpunt is iets dat het volgende kan zijn:

  • Geselecteerd door de URL en HTTP-methode te koppelen.
  • Uitgevoerd door de gemachtigde uit te voeren.

Eindpunten die door de app kunnen worden vergeleken en uitgevoerd, worden geconfigureerd in UseEndpoints. MapGet MapPost Vergelijkbare methoden verbinden aanvraagafgevaardigden met het routeringssysteem. Aanvullende methoden kunnen worden gebruikt om ASP.NET Core-frameworkfuncties te verbinden met het routeringssysteem:

In het volgende voorbeeld ziet u routering met een geavanceerdere routesjabloon:

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/hello/{name:alpha}", async context =>
    {
        var name = context.Request.RouteValues["name"];
        await context.Response.WriteAsync($"Hello {name}!");
    });
});

De tekenreeks /hello/{name:alpha} is een routesjabloon. Het wordt gebruikt om te configureren hoe het eindpunt wordt gematcht. In dit geval komt de sjabloon overeen met:

  • Een URL zoals /hello/Ryan
  • Elk URL-pad dat begint met /hello/ gevolgd door een reeks alfabetische tekens. :alpha past een routebeperking toe die alleen overeenkomt met alfabetische tekens. Routebeperkingen worden verderop in dit document uitgelegd.

Het tweede segment van het URL-pad: {name:alpha}

Het eindpuntrouteringssysteem dat in dit document wordt beschreven, is nieuw vanaf ASP.NET Core 3.0. Alle versies van ASP.NET Core ondersteunen echter dezelfde set functies en routebeperkingen voor routesjablonen.

In het volgende voorbeeld ziet u routering met statuscontroles en autorisatie:

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

Als u codeopmerkingen wilt zien die zijn vertaald naar andere talen dan Engels, laat het ons dan weten in dit GitHub-discussieprobleem.

In het voorgaande voorbeeld ziet u hoe:

  • De middleware voor autorisatie kan worden gebruikt met routering.
  • Eindpunten kunnen worden gebruikt om autorisatiegedrag te configureren.

De MapHealthChecks aanroep voegt een statuscontrole-eindpunt toe. Door RequireAuthorization aan deze oproep toe te voegen, wordt een autorisatiebeleid aan het eindpunt toegevoegd.

Door UseAuthentication en UseAuthorization aan te roepen, wordt de verificatie- en autorisatie-middleware toegevoegd. Deze middleware worden geplaatst tussen UseRouting en UseEndpoints zodat ze het volgende kunnen:

  • Zien welk eindpunt is geselecteerd door UseRouting.
  • Pas een autorisatiebeleid toe voordat UseEndpoints naar het eindpunt wordt verzonden.

Endpoint metadata

In het voorgaande voorbeeld zijn er twee eindpunten, maar alleen het eindpunt voor de statuscontrole heeft een autorisatiebeleid gekoppeld. Als de aanvraag overeenkomt met het eindpunt van de statuscontrole, /healthzwordt er een autorisatiecontrole uitgevoerd. Dit laat zien dat er extra gegevens aan eindpunten kunnen worden gekoppeld. Deze extra gegevens worden eindpuntmetagegevens genoemd:

  • De metagegevens kunnen worden verwerkt door middel van routeringsbewuste middleware.
  • De metagegevens kunnen van elk .NET-type zijn.

Routing concepts

Het routeringssysteem bouwt voort op de middleware-pijplijn door het krachtige eindpuntconcept toe te voegen. Eindpunten vertegenwoordigen eenheden van de functionaliteit van de app die van elkaar verschillen wat betreft routering, autorisatie en een willekeurig aantal ASP.NET Core-systemen.

ASP.NET Kerneindpuntdefinitie

Een ASP.NET Core-eindpunt is:

De volgende code laat zien hoe u het eindpunt ophaalt en inspecteert dat overeenkomt met de huidige aanvraag:

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

Het eindpunt, indien geselecteerd, kan worden opgehaald uit de HttpContext. De eigenschappen kunnen worden geïnspecteerd. Eindpuntobjecten zijn onveranderbaar en kunnen niet worden gewijzigd na het maken. Het meest voorkomende type eindpunt is een RouteEndpoint. RouteEndpoint bevat informatie waarmee deze kan worden geselecteerd door het routeringssysteem.

In de voorgaande code configureert app.Use een inline middleware.

In de volgende code blijkt dat er, afhankelijk van waar in de pijplijn app.Use wordt aangeroepen, mogelijk geen eindpunt is.

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

In dit voorgaande voorbeeld worden instructies toegevoegd Console.WriteLine die aangeven of er al dan niet een eindpunt is geselecteerd. Ter duidelijkheid wijst het voorbeeld een weergavenaam toe aan het opgegeven / eindpunt.

Als u deze code uitvoert met een URL van /, wordt:

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

Als u deze code uitvoert met een andere URL, wordt het volgende weergegeven:

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

Deze uitvoer laat zien dat:

  • Het eindpunt is altijd null voordat UseRouting wordt aangeroepen.
  • Als er een overeenkomst wordt gevonden, is het eindpunt niet null tussen UseRouting en UseEndpoints.
  • De UseEndpoints middleware is terminal wanneer er een overeenkomst wordt gevonden. Terminal-middleware wordt verderop in dit document gedefinieerd.
  • Na UseEndpoints wordt de middleware alleen uitgevoerd als er geen overeenkomst wordt gevonden.

De UseRouting middleware gebruikt de Methode SetEndpoint om het eindpunt aan de huidige context te koppelen. Het is mogelijk om de UseRouting middleware te vervangen door aangepaste logica en nog steeds de voordelen te krijgen van het gebruik van eindpunten. Eindpunten zijn een laag-niveau primitief zoals middleware en zijn niet gekoppeld aan de routeringsimplementatie. De meeste apps hoeven UseRouting niet te vervangen door aangepaste logica.

De UseEndpoints middleware is ontworpen om te worden gebruikt in combinatie met de UseRouting middleware. De kernlogica voor het uitvoeren van een eindpunt is niet ingewikkeld. Gebruik GetEndpoint om het eindpunt op te halen en roep vervolgens de eigenschap van RequestDelegate aan.

De volgende code laat zien hoe middleware invloed kan hebben op of kan reageren op routering:

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

In het voorgaande voorbeeld ziet u twee belangrijke concepten:

  • Middleware kan worden uitgevoerd voordat UseRouting om de gegevens waarop routering plaatsvindt te wijzigen.
  • Middleware kan worden uitgevoerd tussen UseRouting en UseEndpoints om de resultaten van routering te verwerken voordat het eindpunt wordt uitgevoerd.
    • Middleware die wordt uitgevoerd tussen UseRouting en UseEndpoints:
      • Inspecteert meestal metagegevens om inzicht te hebben in de eindpunten.
      • Vaak neemt u beveiligingsbeslissingen, zoals gedaan door UseAuthorization en UseCors.
    • Met de combinatie van middleware en metagegevens kunt u beleidsregels per eindpunt configureren.

In de voorgaande code ziet u een voorbeeld van een aangepaste middleware die beleid per eindpunt ondersteunt. De middleware schrijft een auditlog van de toegang tot gevoelige gegevens naar de console. De middleware kan worden geconfigureerd om een eindpunt te controleren met de AuditPolicyAttribute metagegevens. In dit voorbeeld ziet u een opt-in-patroon waarbij alleen eindpunten die als gevoelig zijn gemarkeerd, worden gecontroleerd. Het is mogelijk om deze logica omgekeerd te definiëren en alles te controleren dat niet als veilig is gemarkeerd, bijvoorbeeld. Het eindpuntmetagegevenssysteem is flexibel. Deze logica kan op welke manier dan ook worden ontworpen voor de use-case.

De voorgaande voorbeeldcode is bedoeld om de basisconcepten van eindpunten te demonstreren. Het voorbeeld is niet bedoeld voor productiegebruik. Een volledigere versie van een middleware voor auditlogboeken zou het volgende doen:

  • Meld u aan bij een bestand of database.
  • Neem details op, zoals de gebruiker, het IP-adres, de naam van het gevoelige eindpunt en meer.

De metagegevens AuditPolicyAttribute van het controlebeleid worden gedefinieerd als een Attribute voor eenvoudiger gebruik met frameworks op basis van klassen, zoals controllers en SignalR. Wanneer u route naar code gebruikt:

  • Metagegevens worden gekoppeld aan een builder-API.
  • Frameworks op basis van klassen bevatten alle kenmerken van de bijbehorende methode en klasse bij het maken van eindpunten.

De aanbevolen procedures voor metagegevenstypen zijn om ze te definiëren als interfaces of kenmerken. Interfaces en kenmerken staan hergebruik van code toe. Het metagegevenssysteem is flexibel en legt geen beperkingen op.

Een terminal-middleware en routering vergelijken

Het volgende codevoorbeeld laat het contrast zien tussen het gebruik van middleware en het gebruik van routering.

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

De stijl van middleware die wordt weergegeven met Approach 1: is terminal-middleware. Dit wordt terminal-middleware genoemd omdat er een overeenkomende bewerking wordt uitgevoerd:

  • De overeenkomende bewerking in het voorgaande voorbeeld is Path == "/" voor de middleware en Path == "/Movie" voor routering.
  • Wanneer een overeenkomst succesvol is, wordt bepaalde functionaliteit uitgevoerd en wordt er teruggekeerd zonder dat de next middleware wordt aangeroepen.

Dit wordt terminal middleware genoemd omdat de zoekopdracht wordt beëindigd, bepaalde functionaliteit wordt uitgevoerd en vervolgens wordt geretourneerd.

Een terminal-middleware en routering vergelijken:

  • Beide benaderingen maken het beëindigen van de verwerkingspijplijn mogelijk:
    • Middleware beëindigt de pijplijn door te retourneren in plaats van aan te nextroepen.
    • Eindpunten zijn altijd terminal.
  • Met terminal-middleware kunt u de middleware op een willekeurige plaats in de pijplijn plaatsen:
    • Eindpunten worden uitgevoerd op de positie van UseEndpoints.
  • Met terminal-middleware kan willekeurige code worden bepaald wanneer de middleware overeenkomt:
    • Aangepaste routingcode kan langdradig zijn en moeilijk correct te schrijven.
    • Routering biedt eenvoudige oplossingen voor typische apps. Voor de meeste apps is geen aangepaste routekoppelingscode vereist.
  • Eindpuntinterface met middleware zoals UseAuthorization en UseCors.
    • Het gebruik van een terminal-middleware met UseAuthorization of UseCors vereist handmatige interfacing met het autorisatiesysteem.

Een eindpunt definieert beide:

  • Een gemachtigde voor het verwerken van aanvragen.
  • Een verzameling willekeurige metagegevens. De metagegevens worden gebruikt om kruislingse problemen te implementeren op basis van beleidsregels en configuratie die aan elk eindpunt zijn gekoppeld.

Terminal-middleware kan een effectief hulpprogramma zijn, maar kan het volgende vereisen:

  • Een aanzienlijke hoeveelheid coderen en testen.
  • Handmatige integratie met andere systemen om het gewenste flexibiliteitsniveau te bereiken.

Overweeg om te integreren met routering voordat u een terminal-middleware schrijft.

Bestaande terminal-middleware die kan worden geïntegreerd met Kaart of MapWhen kan meestal worden omgezet in een routeringsbewust eindpunt. MapHealthChecks demonstreert het patroon voor router-ware:

  • Schrijf een extensiemethode op IEndpointRouteBuilder.
  • Maak een geneste middleware-pijplijn met behulp van CreateApplicationBuilder.
  • Koppel de middleware aan de nieuwe pijplijn. In dit geval UseHealthChecks.
  • Build de middleware-pijplijn naar een RequestDelegate.
  • Roep Map aan en geef de nieuwe middleware-pijplijn op.
  • Het opbouwobject dat door de extensiemethode Map wordt geleverd, wordt geretourneerd.

De volgende code toont het gebruik van 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!");
        });
    });
}

In het voorgaande voorbeeld ziet u waarom het retourneren van het opbouwobject belangrijk is. Als het opbouwobject wordt geretourneerd, kan de app-ontwikkelaar beleidsregels configureren, zoals autorisatie voor het eindpunt. In dit voorbeeld heeft de gezondheidscontroles middleware geen directe integratie met het autorisatiesysteem.

Het metagegevenssysteem is gemaakt als reactie op de problemen die zijn opgetreden door auteurs van uitbreidbaarheid met behulp van terminal-middleware. Het is problematisch voor elke middleware om een eigen integratie met het autorisatiesysteem te implementeren.

URL matching

  • Is het proces waarmee routering overeenkomt met een binnenkomende aanvraag naar een eindpunt.
  • Is gebaseerd op gegevens in het URL-pad en de headers.
  • Kan worden uitgebreid om rekening te houden met alle gegevens in de aanvraag.

Wanneer een routing-middleware wordt uitgevoerd, worden een Endpoint en routewaarden ingesteld op een verzoekfunctie van het HttpContext huidige verzoek:

Middleware wordt uitgevoerd nadat de routerings-middleware het eindpunt kan inspecteren en actie kan ondernemen. Een autorisatie-middleware kan bijvoorbeeld de metagegevensverzameling van het eindpunt ondervragen voor een autorisatiebeleid. Nadat alle middleware in de pijplijn voor aanvraagverwerking is uitgevoerd, wordt de gemachtigde van het geselecteerde eindpunt aangeroepen.

Het routeringssysteem in eindpuntroutering is verantwoordelijk voor alle verzendbeslissingen. Omdat de middleware beleidsregels toepast op basis van het geselecteerde eindpunt, is het belangrijk dat:

  • Elke beslissing die van invloed kan zijn op verzending of de toepassing van beveiligingsbeleid wordt gemaakt in het routeringssysteem.

Warning

Voor achterwaartse compatibiliteit, wanneer een controller- of pagina-eindpuntgedelegeerde wordt uitgevoerd, worden de eigenschappen van Razor ingesteld op de juiste waarden op basis van de verwerking van het verzoek tot nu toe.

Het RouteContext type wordt gemarkeerd als verouderd in een toekomstige release:

  • Migreren RouteData.Values naar HttpRequest.RouteValues.
  • Migreer RouteData.DataTokens om IDataTokensMetadata op te halen uit de metagegevens van het eindpunt.

URL-vergelijking werkt in een configureerbare set fasen. In elke fase is de uitvoer een verzameling van overeenkomsten. De reeks wedstrijden kan verder worden beperkt door de volgende fase. De implementatie van routering garandeert geen verwerkingsvolgorde voor overeenkomende eindpunten. Alle mogelijke overeenkomsten worden in één keer verwerkt. De fasen van het overeenkomen van de URL vinden plaats in de volgende volgorde. ASP.NET Core:

  1. Verwerkt het URL-pad op basis van de set eindpunten en de bijbehorende routesjablonen, en verzamelt alle overeenkomsten.
  2. Neemt de voorgaande lijst, en verwijdert overeenkomsten die niet voldoen aan de toegepaste routebeperkingen.
  3. Neemt de voorgaande lijst en verwijdert overeenkomsten die niet voldoen aan de reeks MatcherPolicy-instanties.
  4. Hiermee gebruikt u de EndpointSelector om een uiteindelijke beslissing te nemen uit de voorgaande lijst.

De lijst met eindpunten wordt gerangschikt op basis van:

Alle overeenkomende eindpunten worden in elke fase verwerkt totdat het EndpointSelector is bereikt. Het EndpointSelector is de laatste fase. Het systeem kiest het eindpunt met de hoogste prioriteit van de overeenkomsten als beste overeenkomst. Als er andere overeenkomsten zijn met dezelfde prioriteit als de beste overeenkomst, wordt er een dubbelzinnige match-exceptie opgeworpen.

De routeprioriteit wordt berekend op basis van een specifiekere routesjabloon die een hogere prioriteit krijgt. Denk bijvoorbeeld aan de sjablonen /hello en /{message}:

  • Beide komen overeen met het URL-pad /hello.
  • /hello is specifieker en dus hogere prioriteit.

Over het algemeen doet routeprioriteit goed werk bij het kiezen van de beste match voor de soorten URL-schema's die in de praktijk worden gebruikt. Gebruik Order deze functie alleen wanneer dat nodig is om dubbelzinnigheid te voorkomen.

Vanwege de soorten uitbreidbaarheid die wordt geboden door routering, is het niet mogelijk dat het routeringssysteem van tevoren de ambigu routes berekent. Bekijk een voorbeeld zoals de routesjablonen /{message:alpha} en /{message:int}:

  • De alpha beperking komt alleen overeen met alfabetische tekens.
  • De int beperking komt alleen overeen met getallen.
  • Deze sjablonen hebben dezelfde routeprioriteit, maar er is geen enkele URL die beide overeenkomen.
  • Als het routeringssysteem bij het opstarten een dubbelzinnigheidsfout heeft gerapporteerd, wordt deze geldige use-case geblokkeerd.

Warning

De volgorde van bewerkingen binnen UseEndpoints heeft geen invloed op het gedrag van routering, met één uitzondering. MapControllerRoute en MapAreaRoute wijs automatisch een orderwaarde toe aan hun eindpunten op basis van de volgorde die ze worden aangeroepen. Dit simuleert langdurige gedrag van controllers zonder dat het routeringssysteem dezelfde garanties biedt als oudere routeringsimplementaties.

In de verouderde implementatie van routering is het mogelijk om routeringsuitbreidbaarheid te implementeren die afhankelijk is van de volgorde waarin routes worden verwerkt. Eindpuntroutering in ASP.NET Core 3.0 of hoger:

  • Heeft geen concept van routes.
  • Biedt geen bestelgaranties. Alle eindpunten worden tegelijk verwerkt.

Prioriteit van routesjabloon en volgorde van eindpuntselectie

Prioriteit van routesjabloon is een systeem dat elke routesjabloon een waarde toewijst op basis van hoe specifiek het is. Prioriteit van routesjabloon:

  • Vermijdt de noodzaak om de volgorde van eindpunten in veelvoorkomende gevallen aan te passen.
  • Pogingen om overeen te komen met de algemene verwachtingen van routeringsgedrag.

Denk bijvoorbeeld aan sjablonen /Products/List en /Products/{id}. Het zou redelijk zijn om ervan uit te gaan dat /Products/List een betere overeenkomst is dan /Products/{id} voor het URL-pad /Products/List. Dit werkt omdat het letterlijke segment /List wordt beschouwd als een betere prioriteit dan het parametersegment /{id}.

De details van hoe prioriteit werkt, zijn gekoppeld aan de wijze waarop routesjablonen worden gedefinieerd:

  • Sjablonen met meer segmenten worden als specifieker beschouwd.
  • Een segment met letterlijke tekst wordt beschouwd als specifieker dan een parametersegment.
  • Een parametersegment met een beperking wordt beschouwd als specifieker dan één zonder.
  • Een complex segment wordt beschouwd als even specifiek als een parametersegment met een beperking.
  • Catch-all-parameters zijn het minst specifiek. Zie catch-all in de routesjabloonverwijzing voor belangrijke informatie over catch-all-routes.

Zie de broncode op GitHub voor een verwijzing naar exacte waarden.

Concepten voor het genereren van URL's

URL generation:

  • Is het proces waarmee routering een URL-pad kan maken op basis van een set routewaarden.
  • Hiermee is een logische scheiding mogelijk tussen eindpunten en de URL's die er toegang toe hebben.

Eindpuntroutering omvat de LinkGenerator API. LinkGenerator is een singleton-service die beschikbaar is via DI. De LinkGenerator API kan buiten de context van een uitvoeringsaanvraag worden gebruikt. Mvc.IUrlHelper en scenario's die afhankelijk zijn IUrlHelpervan, zoals Tag Helpers, HTML Helpers en Actieresultaten, gebruiken de LinkGenerator API intern om mogelijkheden voor het genereren van koppelingen te bieden.

De koppelingsgenerator wordt ondersteund door het concept van adres- en adresschema's. Een adresschema is een manier om de eindpunten te bepalen die moeten worden overwogen voor het genereren van koppelingen. De scenario's van routenaam en routewaarden die voor veel gebruikers bekend zijn, worden met controllers en Razor Pagina's geïmplementeerd als een adresschema.

De koppelingsgenerator kan via de volgende uitbreidingsmethoden een koppeling maken naar controllers en Razor pagina's:

Overbelastingen van deze methoden accepteren argumenten die de HttpContext. Deze methoden zijn functioneel gelijkwaardig aan Url.Action en Url.Page, maar bieden extra flexibiliteit en opties.

De GetPath* methoden zijn het meest vergelijkbaar met Url.Action en Url.Page, omdat ze een URI genereren die een absoluut pad bevat. De GetUri* methoden genereren altijd een absolute URI die een schema en host bevat. De methoden die een HttpContext accepteren, genereren een URI in de context van de lopende aanvraag. De omgevingsroutewaarden , het URL-basispad, het schema en de host van de uitvoerbare aanvraag worden gebruikt, tenzij deze worden overschreven.

LinkGenerator kan worden aangeroepen met een adres. Het genereren van een URI vindt plaats in twee stappen:

  1. Een adres is gebonden aan een lijst met eindpunten die overeenkomen met het adres.
  2. De eindpunten RoutePattern worden geëvalueerd totdat een routepatroon dat overeenkomt met de opgegeven waarden wordt gevonden. De resulterende uitvoer wordt gecombineerd met de andere URI-onderdelen die aan de koppelingsgenerator worden geleverd en geretourneerd.

De methoden die door LinkGenerator worden geboden, ondersteunen het genereren van standaardkoppelingen voor elk type adres. De handigste manier om de koppelingsgenerator te gebruiken, is via extensiemethoden die bewerkingen uitvoeren voor een specifiek adrestype:

Extension Method Description
GetPathByAddress Genereert een URI met een absoluut pad op basis van de opgegeven waarden.
GetUriByAddress Genereert een absolute URI op basis van de opgegeven waarden.

Warning

Let op de volgende gevolgen van het aanroepen LinkGenerator van methoden:

  • Gebruik GetUri* extensiemethoden met voorzichtigheid in een app-configuratie die de Host header van binnenkomende aanvragen niet valideert. Als de Host header van binnenkomende aanvragen niet wordt gevalideerd, kan niet-vertrouwde aanvraaginvoer worden teruggestuurd naar de client in URI's in een weergave of pagina. Het is raadzaam dat alle productie-apps hun server configureren om de Host header te valideren op basis van bekende geldige waarden.

  • Wees LinkGenerator voorzichtig met middleware in combinatie met Map of MapWhen. Map* wijzigt het basispad van de uitvoerende aanvraag, wat van invloed is op de uitvoer van linkgeneratie. Alle API's maken het mogelijk om LinkGenerator een basispad op te geven. Geef een leeg basispad op om het effect bij het Map* genereren van koppelingen ongedaan te maken.

Middleware example

In het volgende voorbeeld gebruikt een middleware de LinkGenerator API om een koppeling te maken naar een actiemethode met een lijst met winkelproducten. Het gebruik van de koppelingsgenerator door deze in een klasse te injecteren en aanroepen GenerateLink is beschikbaar voor elke klasse in een 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.");
    }
}

Naslagwerk routesjablonen

Tokens binnen {} definiëren routeparameters die worden gebonden als de route overeenkomt. Er kunnen meer dan één routeparameter worden gedefinieerd in een routesegment, maar routeparameters moeten worden gescheiden door een letterlijke waarde. Bijvoorbeeld, {controller=Home}{action=Index} is geen geldige route, omdat er geen letterlijke waarde is tussen {controller} en {action}. Routeparameters moeten een naam hebben en kunnen aanvullende kenmerken hebben opgegeven.

Letterlijke tekst anders dan routeparameters (bijvoorbeeld {id}) en het padscheidingsteken / moet overeenkomen met de tekst in de URL. Tekstvergelijking is niet hoofdlettergevoelig en is gebaseerd op de gedecodeerde weergave van het pad van de URL. Als u een letterlijk routeparameterscheidingsteken { wilt vergelijken of }, escapet u het scheidingsteken door het teken te herhalen. Bijvoorbeeld {{ of }}.

Sterretje * of dubbel sterretje **:

  • Kan worden gebruikt als voorvoegsel voor een routeparameter om verbinding te maken met de rest van de URI.
  • Worden een catch-all-parameters genoemd. Bijvoorbeeld: blog/{**slug}
    • Komt overeen met elke URI die begint met /blog en die een waarde bevat die erop volgt.
    • De waarde die volgt op /blog wordt toegewezen aan de slug routewaarde.

Warning

Een catch-all--parameter kan verkeerde routes matchen door een fout in de routering. Apps die door deze fout worden beïnvloed, hebben de volgende kenmerken:

  • Een algemene route, bijvoorbeeld {**slug}"
  • De catch-all-route kan niet overeenkomen met aanvragen die moeten overeenkomen.
  • Als u andere routes verwijdert, begint de catch-all route te werken.

Zie GitHub-bugs 18677 en 16579 voor voorbeeldgevallen die deze bug ervaren.

Een opt-in-oplossing voor deze fout is opgenomen in .NET Core 3.1.301 of hoger SDK. Met de volgende code wordt een interne switch ingesteld waarmee deze fout wordt opgelost:

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

Catch-all-parameters kunnen ook overeenkomen met de lege tekenreeks.

De catch-all-parameter escapeert de juiste tekens wanneer de route wordt gebruikt om een URL te genereren, inclusief padscheidingstekens /. De route foo/{*path} met routewaarden { path = "my/path" } genereert foo/my%2Fpathbijvoorbeeld . Noteer de escaped slash. Gebruik het voorvoegsel van de ** routeparameter om het padscheidingsteken af te ronden. De route foo/{**path} met { path = "my/path" } genereert foo/my/path.

URL-patronen die proberen een bestandsnaam met een optionele bestandsextensie vast te leggen, hebben aanvullende overwegingen. Denk bijvoorbeeld aan de sjabloon files/{filename}.{ext?}. Wanneer waarden voor beide filename en ext bestaan, worden beide waarden ingevuld. Als er slechts een waarde voor filename bestaat in de URL, komt de route overeen omdat het volgtraject . optioneel is. De volgende URL's komen overeen met deze route:

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

Routeparameters kunnen standaardwaarden hebben die zijn aangewezen door de standaardwaarde op te geven na de parameternaam gescheiden door een gelijkteken (=). Definieert {controller=Home} bijvoorbeeld Home als de standaardwaarde voor controller. De standaardwaarde wordt gebruikt als er geen waarde aanwezig is in de URL voor de parameter. Routeparameters worden optioneel gemaakt door een vraagteken (?) toe te voegen aan het einde van de parameternaam. Bijvoorbeeld: id?. Het verschil tussen optionele waarden en standaardrouteparameters is:

  • Een routeparameter met een standaardwaarde produceert altijd een waarde.
  • Een optionele parameter heeft alleen een waarde wanneer een waarde wordt opgegeven door de aanvraag-URL.

Routeparameters kunnen beperkingen hebben die overeenkomen met de routewaarde die is gebonden aan de URL. Door : en de naam van de beperking na de naam van de routeparameter toe te voegen, wordt een inline beperking voor een routeparameter gespecificeerd. Als voor de beperking argumenten zijn vereist, staan deze tussen haakjes (...) achter de naam van de beperking. Er kunnen meerdere inlinebeperkingen worden opgegeven door een andere : naam en beperking toe te voegen.

De naam en argumenten van de beperking worden aan de IInlineConstraintResolver-service doorgegeven om een exemplaar van IRouteConstraint te maken, voor gebruik in URL-verwerking. De routesjabloon blog/{article:minlength(10)} geeft bijvoorbeeld een minlength beperking op met het argument 10. Zie de sectie Routebeperkingsreferentie voor meer informatie over routebeperkingen en een lijst met de beperkingen die door het framework worden geboden.

Routeparameters kunnen ook parametertransformatoren hebben. Parametertransformatoren transformeren de waarde van een parameter bij het genereren van koppelingen en overeenkomende acties en pagina's naar URL's. Net zoals beperkingen kunnen parametertransformatoren inline aan een routeparameter worden toegevoegd door na de naam van de routeparameter een : en de naam van de transformer toe te voegen. De routesjabloon blog/{article:slugify} geeft bijvoorbeeld een slugify transformator op. Zie de sectie Naslaginformatie over parametertransformatoren voor meer informatie over parametertransformaties .

In de volgende tabel ziet u voorbeeldroutesjablonen en hun gedrag:

Route Template Voorbeeld van overeenkomende URI De aanvraag-URI...
hello /hello Komt alleen overeen met het ene pad /hello.
{Page=Home} / Koppelt en stelt Page in op Home.
{Page=Home} /Contact Koppelt en stelt Page in op Contact.
{controller}/{action}/{id?} /Products/List Wordt toegewezen aan de Products controller en List actie.
{controller}/{action}/{id?} /Products/Details/123 Wordt toegewezen aan de controller Products en de actie Details, waarbij id is ingesteld op 123.
{controller=Home}/{action=Index}/{id?} / Verwijst naar de Home controller en Index methode. id wordt genegeerd.
{controller=Home}/{action=Index}/{id?} /Products Verwijst naar de Products controller en Index methode. id wordt genegeerd.

Het gebruik van een sjabloon is over het algemeen de eenvoudigste methode voor routering. Beperkingen en standaardwaarden kunnen ook buiten de routesjabloon worden opgegeven.

Complex segments

Complexe segmenten worden verwerkt door letterlijke scheidingstekens van rechts naar links op een niet-gretige wijze te vergelijken. Is bijvoorbeeld [Route("/a{b}c{d}")] een complex segment. Complexe segmenten werken op een bepaalde manier die moet worden begrepen om ze succesvol te kunnen gebruiken. In het voorbeeld in deze sectie ziet u waarom complexe segmenten alleen goed werken wanneer de tekst van het scheidingsteken niet binnen de parameterwaarden wordt weergegeven. Het gebruik van een regex en het handmatig extraheren van de waarden is nodig voor complexere gevallen.

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Dit is een samenvatting van de stappen die routering uitvoert met de sjabloon /a{b}c{d} en het URL-pad /abcd. De | functie wordt gebruikt om te visualiseren hoe het algoritme werkt:

  • De eerste letterlijke waarde, van rechts naar links, is c. Dus /abcd wordt vanaf rechts gezocht en vindt /ab|c|d.
  • Alles aan de rechterkant (d) komt nu overeen met de routeparameter {d}.
  • De volgende letterlijke waarde is a, gelezen van rechts naar links. Dus /ab|c|d wordt gezocht vanaf waar we gebleven waren, dan a wordt gevonden /|a|b|c|d.
  • De waarde aan de rechterkant (b) komt nu overeen met de routeparameter {b}.
  • Er is geen resterende tekst en geen resterende routesjabloon, dus dit komt overeen.

Hier volgt een voorbeeld van een negatief geval met dezelfde sjabloon /a{b}c{d} en het URL-pad /aabcd. De | functie wordt gebruikt om te visualiseren hoe het algoritme werkt. Dit geval is geen overeenkomst, wat wordt uitgelegd door hetzelfde algoritme:

  • De eerste letterlijke waarde, van rechts naar links, is c. Dus /aabcd wordt vanaf rechts gezocht en vindt /aab|c|d.
  • Alles aan de rechterkant (d) komt nu overeen met de routeparameter {d}.
  • De volgende letterlijke waarde is a, gelezen van rechts naar links. Dus /aab|c|d wordt gezocht vanaf waar we gebleven waren, dan a wordt gevonden /a|a|b|c|d.
  • De waarde aan de rechterkant (b) komt nu overeen met de routeparameter {b}.
  • Op dit moment is er nog tekst a, maar het algoritme heeft geen routesjabloon meer om te parseren, dus dit is geen overeenkomst.

Omdat het overeenkomende algoritme niet-graaiend is:

  • Deze komt overeen met de kleinste hoeveelheid tekst die in elke stap mogelijk is.
  • Elk geval waarin de scheidingstekenwaarde in de parameterwaarden wordt weergegeven, resulteert in niet-overeenkomende waarden.

Reguliere expressies bieden veel meer controle over hun overeenkomende gedrag.

Greedy matching, ook wel lazy matching genoemd, komt overeen met de grootst mogelijke tekenreeks. Niet-greedy komt overeen met de kleinste mogelijke tekenreeks.

Verwijzing naar routebeperking

Routebeperkingen worden uitgevoerd wanneer er een overeenkomst is opgetreden met de binnenkomende URL en het URL-pad wordt omgezet in routewaarden. Routebeperkingen inspecteren doorgaans de routewaarde die via de routesjabloon is gekoppeld en bepalen of de waarde acceptabel is door een waar of onwaar beslissing te maken. Bij sommige routebeperkingen worden gegevens buiten de routewaarde gebruikt om na te gaan of de aanvraag kan worden gerouteerd. Bijvoorbeeld kan de HttpMethodRouteConstraint een aanvraag accepteren of weigeren op basis van het HTTP-werkwoord. Beperkingen worden gebruikt bij het genereren van routeringsaanvragen en het genereren van koppelingen.

Warning

Gebruik geen beperkingen voor invoervalidatie. Als beperkingen worden gebruikt voor invoervalidatie, resulteert ongeldige invoer in een 404 antwoord niet gevonden. Ongeldige invoer moet een 400 ongeldige aanvraag opleveren met een geschikt foutbericht. Routebeperkingen worden gebruikt om vergelijkbare routes te ontkoppelen, niet om de invoer voor een bepaalde route te valideren.

In de volgende tabel ziet u voorbeeld van routebeperkingen en hun verwachte gedrag:

constraint Example Example Matches Notes
int {id:int} 123456789, -123456789 Komt overeen met een geheel getal
bool {active:bool} true, FALSE Komt overeen met true of false. Case-insensitive
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm Komt overeen met een geldige DateTime waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
decimal {price:decimal} 49.99, -1,000.01 Komt overeen met een geldige decimal waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
double {weight:double} 1.234, -1,001.01e8 Komt overeen met een geldige double waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
float {weight:float} 1.234, -1,001.01e8 Komt overeen met een geldige float waarde in de invariante cultuur. Zie de voorgaande waarschuwing.
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638 Komt overeen met een geldige Guid waarde
long {ticks:long} 123456789, -123456789 Komt overeen met een geldige long waarde
minlength(value) {username:minlength(4)} Rick Tekenreeks moet ten minste 4 tekens bevatten
maxlength(value) {filename:maxlength(8)} MyFile Tekenreeks mag niet langer zijn dan 8 tekens
length(length) {filename:length(12)} somefile.txt String moet exact 12 tekens lang zijn
length(min,max) {filename:length(8,16)} somefile.txt Een reeks moet minimaal 8 en niet meer dan 16 tekens lang zijn
min(value) {age:min(18)} 19 De waarde van het gehele getal moet ten minste 18 zijn
max(value) {age:max(120)} 91 Een geheel getal mag niet meer dan 120 zijn
range(min,max) {age:range(18,120)} 91 Een geheel getal moet ten minste 18 zijn, maar niet meer dan 120
alpha {name:alpha} Rick Een tekenreeks moet uit één of meer alfabetische tekens a-z bestaan en is hoofdletterongevoelig.
regex(expression) {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} 123-45-6789 De tekenreeks moet overeenkomen met de reguliere expressie. Zie tips voor het definiëren van een reguliere expressie.
required {name:required} Rick Wordt gebruikt om af te dwingen dat er een niet-parameterwaarde aanwezig is tijdens het genereren van de URL

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Meerdere, door dubbele punt gescheiden beperkingen kunnen worden toegepast op één parameter. Met de volgende beperking wordt bijvoorbeeld een parameter beperkt tot een geheel getal van 1 of hoger:

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

Warning

Routebeperkingen die de URL controleren en worden geconverteerd naar een CLR-type, gebruiken altijd de invariante cultuur. Bijvoorbeeld conversie naar het CLR-type int of DateTime. Bij deze beperkingen wordt ervan uitgegaan dat de URL niet kan worden gelokaliseerd. De door het framework geleverde routebeperkingen wijzigen niet de waarden die zijn opgeslagen in routewaarden. Alle routewaarden die uit de URL worden geparseerd, worden opgeslagen als tekenreeksen. De beperking probeert bijvoorbeeld float de routewaarde te converteren naar een float, maar de geconverteerde waarde wordt alleen gebruikt om te controleren of deze kan worden geconverteerd naar een float.

Reguliere expressies in beperkingen

Warning

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

Reguliere expressies kunnen worden opgegeven als inlinebeperkingen met behulp van de regex(...) routebeperking. Methoden in de MapControllerRoute familie accepteren ook een object-literal met beperkingen. Als dit formulier wordt gebruikt, worden tekenreekswaarden geïnterpreteerd als reguliere expressies.

De volgende code maakt gebruik van een inline regex-beperking:

app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
        context => 
        {
            return context.Response.WriteAsync("inline-constraint match");
        });
 });

In de volgende code wordt een letterlijk object gebruikt om een regex-beperking op te geven:

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

Het ASP.NET Core-framework voegt RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant toe aan de reguliere expressie-constructor. Zie RegexOptions voor een beschrijving van deze leden.

Reguliere expressies maken gebruik van scheidingstekens en tokens die vergelijkbaar zijn met tokens die worden gebruikt door routering en de C#-taal. Tokens voor reguliere expressies moeten worden geëscaped. Als u de reguliere expressie ^\d{3}-\d{2}-\d{4}$ in een inlinebeperking wilt gebruiken, gebruikt u een van de volgende opties:

  • Vervang \ tekens in de tekenreeks als \\ tekens in het C#-bronbestand om het escapeteken voor tekenreeksen \ te escapen.
  • Verbatim tekenreeksen.

Als u tekens voor scheidingstekens van routeringsparameters wilt escapen, verdubbel dan de tekens in de expressie, bijvoorbeeld {, }, [, ], {{, }}, [[, ]]. In de volgende tabel ziet u een reguliere expressie en de escape-versie:

Regular expression Geëscapede reguliere expressie
^\d{3}-\d{2}-\d{4}$ ^\\d{{3}}-\\d{{2}}-\\d{{4}}$
^[a-z]{2}$ ^[[a-z]]{{2}}$

Reguliere expressies die in routering worden gebruikt, beginnen vaak met het ^ teken en komen overeen met de beginpositie van de tekenreeks. De expressies eindigen vaak met het $ teken en komen overeen met het einde van de tekenreeks. De ^ en $ tekens zorgen ervoor dat de reguliere expressie overeenkomt met de volledige waarde van de routeparameter. Zonder de ^ en $ tekens komt de reguliere expressie overeen met een subtekenreeks binnen de tekenreeks, wat vaak ongewenst is. De volgende tabel bevat voorbeelden en legt uit waarom ze overeenkomen of niet overeenkomen:

Expression String Match Comment
[a-z]{2} hello Yes Substring matches
[a-z]{2} 123abc456 Yes Substring matches
[a-z]{2} mz Yes Matches expression
[a-z]{2} MZ Yes Niet hoofdlettergevoelig
^[a-z]{2}$ hello No Zie ^ en $ hierboven
^[a-z]{2}$ 123abc456 No Zie ^ en $ hierboven

Zie .NET Framework Regular Expressions voor meer informatie over de syntaxis van reguliere expressies.

Als u een parameter wilt beperken tot een bekende set mogelijke waarden, gebruikt u een reguliere expressie. Komt bijvoorbeeld {action:regex(^(list|get|create)$)} alleen overeen met de action routewaarde naar list, getof create. Wanneer dit wordt doorgegeven aan een woordenlijst met beperkingen, is de tekenreeks ^(list|get|create)$ gelijk. Beperkingen die worden doorgegeven in de woordenlijst met beperkingen die niet overeenkomen met een van de bekende beperkingen, worden ook beschouwd als reguliere expressies. Beperkingen die worden doorgegeven binnen een sjabloon die niet overeenkomen met een van de bekende beperkingen, worden niet behandeld als reguliere expressies.

Aangepaste routebeperkingen

Aangepaste routebeperkingen kunnen worden gemaakt door de IRouteConstraint interface te implementeren. De IRouteConstraint interface bevat Match, die retourneert true als aan de beperking wordt voldaan en false anders.

Aangepaste routebeperkingen zijn zelden nodig. Voordat u een aangepaste routebeperking implementeert, moet u alternatieven overwegen, zoals modelbinding.

De map ASP.NET Kernbeperkingen biedt goede voorbeelden van het maken van beperkingen. Bijvoorbeeld GuidRouteConstraint.

Als u een aangepast type IRouteConstraintroutebeperking wilt gebruiken, moet het type routebeperking worden geregistreerd bij de app ConstraintMap in de servicecontainer. Een ConstraintMap is een woordenlijst die routebeperkingssleutels toe wijst aan IRouteConstraint implementaties die deze beperkingen valideren. De ConstraintMap van een app kan in Startup.ConfigureServices worden bijgewerkt, hetzij als onderdeel van een services.AddRouting-oproep, hetzij door RouteOptions direct met services.Configure<RouteOptions> te configureren. For example:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddRouting(options =>
    {
        options.ConstraintMap.Add("customName", typeof(MyCustomConstraint));
    });
}

De voorgaande beperking wordt toegepast in de volgende code:

[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 wordt geleverd door het Rick.Docs.Samples.RouteInfo NuGet-pakket en toont routegegevens.

De implementatie van MyCustomConstraint voorkomt dat 0 deze wordt toegepast op een routeparameter:

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

Wanneer u System.Text.RegularExpressions gebruikt om niet-vertrouwde invoer te verwerken, geeft u een time-out door. Een kwaadwillende gebruiker kan invoer opgeven om een RegularExpressions te veroorzaken. ASP.NET Core framework-API's die gebruikmaken van RegularExpressions geven een time-out door.

De voorgaande code:

  • Voorkomt 0 in het {id} segment van de route.
  • Wordt weergegeven om een eenvoudig voorbeeld te geven van het implementeren van een aangepaste beperking. Deze mag niet worden gebruikt in een productie-app.

De volgende code is een betere benadering om te voorkomen dat een id met een 0 wordt verwerkt.

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

    return ControllerContext.MyDisplayRouteInfo(id);
}

De voorgaande code heeft de volgende voordelen ten opzichte van de MyCustomConstraint aanpak:

  • Er is geen aangepaste beperking vereist.
  • Het retourneert een meer beschrijvende fout wanneer de routeparameter 0 bevat.

Parametertransformatorreferentie

Parameter transformers:

Een aangepaste slugify parametertransformator in routepatroon blog\{article:slugify} met Url.Action(new { article = "MyTestArticle" }) genereert blog\my-test-articlebijvoorbeeld .

Houd rekening met de volgende IOutboundParameterTransformer implementatie:

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

Als u een parametertransformatie in een routepatroon wilt gebruiken, configureert u deze met behulp van ConstraintMapStartup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddRouting(options =>
    {
        options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
    });
}

Het ASP.NET Core-framework gebruikt parametertransformatoren om de URI te transformeren waarop een eindpunt wordt afgerond. Parametertransformatoren transformeren bijvoorbeeld de routewaarden die worden gebruikt om een area, controller, action, en page.

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

Met de voorgaande routesjabloon wordt de actie SubscriptionManagementController.GetAll vergeleken met de URI /subscription-management/get-all. Een parametertransformator wijzigt niet de routewaarden die worden gebruikt om een koppeling te genereren. Bijvoorbeeld Url.Action("GetAll", "SubscriptionManagement") produceert /subscription-management/get-all.

ASP.NET Core biedt API-conventies voor het gebruik van parametertransformatoren met gegenereerde routes:

Naslaginformatie over het genereren van URL's

Deze sectie bevat een verwijzing voor het algoritme dat is geïmplementeerd door het genereren van url's. In de praktijk gebruiken de meeste complexe voorbeelden van het genereren van URL's controllers of Razor pagina's. Zie routering in controllers voor meer informatie.

Het proces voor het genereren van url's begint met een aanroep naar LinkGenerator.GetPathByAddress of een vergelijkbare methode. De methode wordt geleverd met een adres, een set routewaarden en eventueel informatie over de huidige aanvraag van HttpContext.

De eerste stap is het gebruik van het adres om een set kandidaat-eindpunten op te lossen met behulp van een IEndpointAddressScheme<TAddress> eindpunt dat overeenkomt met het type adres.

Zodra de set kandidaten door het adresschema is gevonden, worden de eindpunten iteratief gerangschikt en verwerkt totdat een URL-generatiebewerking is geslaagd. Het genereren van url's controleert niet op dubbelzinnigheden. Het eerste resultaat dat wordt geretourneerd, is het uiteindelijke resultaat.

Problemen bij het genereren van URL's met logboekregistratie oplossen

De eerste stap bij het oplossen van problemen met URL-generatie is het instellen van het niveau van de logboekregistratie op Microsoft.AspNetCore.RoutingTRACE. LinkGenerator registreert veel details over de verwerking die nuttig kan zijn om problemen op te lossen.

Zie naslaginformatie over het genereren van URL's voor meer informatie over het genereren van url's.

Addresses

Adressen zijn het concept in het genereren van URL's dat wordt gebruikt om een aanroep aan de koppelingsgenerator te binden aan een set kandidaat-eindpunten.

Adressen zijn een uitbreidbaar concept dat standaard wordt geleverd met twee implementaties:

  • Gebruik de eindpuntnaam (string) als het adres:
    • Biedt vergelijkbare functionaliteit als de routenaam van MVC.
    • Maakt gebruik van het IEndpointNameMetadata metagegevenstype.
    • Hiermee wordt de opgegeven reeks afgestemd op de metagegevens van alle geregistreerde eindpunten.
    • Genereert een uitzondering bij het opstarten als meerdere eindpunten dezelfde naam gebruiken.
    • Aanbevolen voor algemeen gebruik buiten controllers en Razor pagina's.
  • Routewaarden (RouteValuesAddress) gebruiken als het adres:
    • Biedt vergelijkbare functionaliteit voor controllers en Razor pagina's voor het genereren van verouderde URL's.
    • Zeer complex om uit te breiden en fouten op te sporen.
    • Biedt de implementatie die wordt gebruikt door IUrlHelper, Tag Helpers, HTML Helpers, Actieresultaten, enzovoort.

De rol van het adresschema is het maken van de koppeling tussen het adres en overeenkomende eindpunten op willekeurige criteria:

  • Het eindpuntnaamschema voert een eenvoudige woordenboekopzoeking uit.
  • Het schema voor routewaarden heeft een complex algoritme voor het bepalen van de beste subset van een set.

Omgevingswaarden en expliciete waarden

Vanuit de huidige aanvraag heeft routering toegang tot de routewaarden van de huidige aanvraag HttpContext.Request.RouteValues. De waarden die aan de huidige aanvraag zijn gekoppeld, worden de omgevingswaarden genoemd. Ter duidelijkheid verwijst de documentatie naar de routewaarden die worden doorgegeven aan methoden als expliciete waarden.

In het volgende voorbeeld ziet u omgevingswaarden en expliciete waarden. Het biedt omgevingswaarden van de huidige aanvraag en expliciete waarden: { 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);
    }

De voorgaande code:

De volgende code bevat geen omgevingswaarden en expliciete waarden: { controller = "Home", action = "Subscribe", id = 17, }

public IActionResult Index2()
{
    var url = _linkGenerator.GetPathByAction("Subscribe", "Home",
                                             new { id = 17, });
    return Content(url);
}

De voorgaande methode retourneert /Home/Subscribe/17

De volgende code in de WidgetController retourneert /Widget/Subscribe/17:

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

De volgende code levert de controller op basis van omgevingswaarden in het huidige verzoek en expliciete waarden: { action = "Edit", id = 17, }

public class GadgetController : Controller
{
    public IActionResult Index()
    {
        var url = Url.Action("Edit", new { id = 17, });
        return Content(url);
    }

In de voorgaande code:

  • /Gadget/Edit/17 wordt geretourneerd.
  • Url haalt de IUrlHelper.
  • Action genereert een URL met een absoluut pad voor een actiemethode. De URL bevat de opgegeven action naam en route waarden.

De volgende code bevat omgevingswaarden van de huidige aanvraag en expliciete waarden: { page = "./Edit, id = 17, }

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

De voorgaande code stelt url in op /Edit/17 wanneer de pagina Bewerken Razor de volgende pagina-instructie bevat:

@page "{id:int}"

Als de pagina Bewerken de "{id:int}" routesjabloon niet bevat, is url/Edit?id=17.

Het gedrag van MVC's IUrlHelper voegt een complexiteitslaag toe, naast de regels die hier worden beschreven:

  • IUrlHelper levert altijd de routewaarden van de huidige aanvraag als omgevingswaarden.
  • IUrlHelper.Action kopieert altijd de huidige action en controller routewaarden als expliciete waarden, tenzij deze worden overschreven door de ontwikkelaar.
  • IUrlHelper.Page kopieert de huidige page routewaarde altijd als een expliciete waarde, tenzij deze wordt overschreven.
  • IUrlHelper.Page overschrijft altijd de huidige handler routewaarde met null als expliciete waarde, tenzij null wordt overschreven.

Gebruikers worden vaak verrast door de gedragsdetails van omgevingswaarden, omdat MVC geen eigen regels lijkt te volgen. Om historische en compatibiliteitsredenen hebben bepaalde routewaarden zoals action, controller, page, en handler hun eigen speciale gedrag.

De equivalente functionaliteit die wordt geleverd door LinkGenerator.GetPathByAction en LinkGenerator.GetPathByPage dupliceert deze afwijkingen van IUrlHelper voor compatibiliteit.

Proces voor het genereren van URL's

Zodra de set kandidaat-eindpunten is gevonden, genereert het algoritme URL's.

  • Hiermee worden de eindpunten iteratief verwerkt.
  • Retourneert het eerste geslaagde resultaat.

De eerste stap in dit proces wordt routewaardevervalsing genoemd. Routewaarde-invalidering is het proces waarmee routering bepaalt welke routewaarden van de ambiente waarden moeten worden gebruikt en welke moeten worden genegeerd. Elke omgevingswaarde wordt beschouwd en gecombineerd met de expliciete waarden of genegeerd.

De beste manier om na te denken over de rol van omgevingswaarden is dat ze proberen toepassingsontwikkelaars te helpen minder te typen in veelvoorkomende gevallen. Normaal gesproken zijn de scenario's waarin omgevingswaarden nuttig zijn gerelateerd aan MVC:

  • Wanneer u een koppeling maakt naar een andere actie in dezelfde controller, hoeft de controllernaam niet te worden opgegeven.
  • Wanneer u een koppeling maakt naar een andere controller in hetzelfde gebied, hoeft de gebiedsnaam niet te worden opgegeven.
  • Bij het koppelen aan dezelfde actiemethode hoeven routewaarden niet te worden opgegeven.
  • Wanneer u een koppeling maakt naar een ander deel van de app, wilt u geen routewaarden overdragen die geen betekenis hebben in dat deel van de app.

Aanroepen naar LinkGenerator of IUrlHelper die terug null geven, worden meestal veroorzaakt door het niet begrijpen van de invalidering van de routewaarde. Los ongeldige routewaarden op door expliciet meer routewaarden op te geven om te zien of het probleem hierdoor wordt opgelost.

Invalidatie van routewaarden werkt volgens de veronderstelling dat het URL-schema van de app hiërarchisch is, met een hiërarchie die van links naar rechts is gevormd. Overweeg de basissjabloon {controller}/{action}/{id?} voor controllerroute om een intuïtief beeld te krijgen van hoe dit in de praktijk werkt. Als u een waarde wijzigt , worden alle routewaarden die rechts worden weergegeven, ongeldig . Dit weerspiegelt de aanname over hiërarchie. Als de app een omgevingswaarde heeft voor id, en de bewerking een andere waarde specificeert voor de controller:

  • id wordt niet hergebruikt omdat {controller} links van {id?} staat.

Enkele voorbeelden waarin dit principe wordt gedemonstreerd:

  • Als de expliciete waarden een waarde bevatten voor id, wordt de omgevingswaarde genegeerd id . De omgevingswaarden voor controller en action kunnen worden gebruikt.
  • Als de expliciete waarden een waarde bevatten voor action, wordt een omgevingswaarde action voor genegeerd. De omgevingswaarden voor controller kunnen worden gebruikt. Als de expliciete waarde action anders is dan de omgevingswaarde, actionwordt de id waarde niet gebruikt. Als de expliciete waarde voor action hetzelfde is als de omgevingswaarde voor action, kan de id waarde worden gebruikt.
  • Als de expliciete waarden een waarde bevatten voor controller, wordt een omgevingswaarde controller voor genegeerd. Als de expliciete waarde controller verschilt van de omgevingswaarde voor controller, worden de action waarden id niet gebruikt. Als de expliciete waarde voor controller dezelfde is als de omgevingswaarde voor controller, kunnen de waarden action en id worden gebruikt.

Dit proces wordt verder gecompliceerd door het bestaan van kenmerkroutes en toegewezen conventionele routes. Conventionele routes, zoals {controller}/{action}/{id?}, geven een hiërarchie aan met behulp van routeparameters. Voor specifieke conventionele routes en attributieroutes naar controllers en Razor pagina's:

  • Er is een hiërarchie met routewaarden.
  • Ze worden niet weergegeven in de sjabloon.

In deze gevallen definieert het genereren van URL's het concept van de vereiste waarden . Eindpunten die zijn gemaakt door controllers en Razor pagina's hebben vereiste waarden opgegeven waarmee routewaardevervalidering kan worden uitgevoerd.

Het invalidatie-algoritme voor de routewaarde in detail:

  • De vereiste waardenamen worden gecombineerd met de routeparameters en worden vervolgens van links naar rechts verwerkt.
  • Voor elke parameter worden de omgevingswaarde en expliciete waarde vergeleken:
    • Als de omgevingswaarde en expliciete waarde hetzelfde zijn, wordt het proces voortgezet.
    • Als de omgevingswaarde aanwezig is en de expliciete waarde niet is, wordt de omgevingswaarde gebruikt bij het genereren van de URL.
    • Als de omgevingswaarde niet aanwezig is en de expliciete waarde is, negeert u de omgevingswaarde en alle volgende omgevingswaarden.
    • Als de omgevingswaarde en de expliciete waarde aanwezig zijn en de twee waarden verschillen, negeert u de omgevingswaarde en alle volgende omgevingswaarden.

Op dit moment is de bewerking voor het genereren van URL's gereed om routebeperkingen te evalueren. De set van geaccepteerde waarden wordt samen met de standaardwaarden van de parameter verstrekt aan de beperkingen. Als aan alle beperkingen is voldaan, wordt de bewerking voortgezet.

Vervolgens kunnen de geaccepteerde waarden worden gebruikt om de routesjabloon uit te vouwen. De routesjabloon wordt verwerkt:

  • From left-to-right.
  • Elke parameter wordt vervangen door de geaccepteerde waarde.
  • Met de volgende speciale gevallen:
    • Als er een waarde ontbreekt in de geaccepteerde waarden en de parameter een standaardwaarde heeft, wordt de standaardwaarde gebruikt.
    • Als er een waarde ontbreekt in de geaccepteerde waarden en de parameter optioneel is, wordt de verwerking voortgezet.
    • Als een routeparameter rechts van een ontbrekende optionele parameter een waarde heeft, mislukt de bewerking.
    • Aaneengesloten parameters met standaardwaarden en optionele parameters worden indien mogelijk samengevouwen.

Waarden die expliciet zijn opgegeven die niet overeenkomen met een segment van de route, worden toegevoegd aan de querytekenreeks. In de volgende tabel ziet u het resultaat wanneer u de routesjabloon {controller}/{action}/{id?}gebruikt.

Ambient Values Explicit Values Result
controller = "Home" action = "Info" /Home/About
controller = "Home" controller = "Order", action = "About" /Order/About
controller = "Home", color = "Rood" action = "Info" /Home/About
controller = "Home" action = "About", color = "Red" /Home/About?color=Red

Problemen met het ongeldig maken van routewaarde

Vanaf ASP.NET Core 3.0 werken sommige URL-generatieschema's die in eerdere ASP.NET Core-versies worden gebruikt, niet goed met het genereren van URL's. Het ASP.NET Core-team is van plan om functies toe te voegen om deze behoeften in een toekomstige release aan te pakken. Voorlopig is de beste oplossing om verouderde routering te gebruiken.

De volgende code toont een voorbeeld van een URL-generatieschema dat niet wordt ondersteund door routering.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute("default", 
                                     "{culture}/{controller=Home}/{action=Index}/{id?}");
    endpoints.MapControllerRoute("blog", "{culture}/{**slug}", 
                                      new { controller = "Blog", action = "ReadPost", });
});

In de voorgaande code wordt de culture routeparameter gebruikt voor lokalisatie. De wens is om de culture parameter altijd als omgevingswaarde te laten accepteren. De culture parameter wordt echter niet geaccepteerd als een omgevingswaarde vanwege de manier waarop vereiste waarden werken:

  • In de "default" routesjabloon bevindt de culture routeparameter zich links van controller, zodat wijzigingen aan controllerculture niet ongeldig maken.
  • Binnen de "blog" routesjabloon wordt de culture routeparameter beschouwd als rechts van controller, die wordt weergegeven in de vereiste waarden.

Eindpuntmetagegevens configureren

De volgende koppelingen bevatten informatie over het configureren van eindpuntmetagegevens:

Hostkoppeling in routes met RequireHost

RequireHost past een beperking toe op de route waarvoor de opgegeven host is vereist. De RequireHost parameter of [Host] kan het volgende zijn:

  • Host: www.domain.com, komt www.domain.com overeen met elke poort.
  • Host met wildcard: *.domain.com, komt overeen met www.domain.com, subdomain.domain.com, of www.subdomain.domain.com op een willekeurige poort.
  • Poort: *:5000, komt overeen met poort 5000 met elke host.
  • Host en poort: www.domain.com:5000 of *.domain.com:5000, komt overeen met host en poort.

Er kunnen meerdere parameters worden opgegeven met of RequireHost[Host]. De beperking komt overeen met hosts die geldig zijn voor een van de parameters. Bijvoorbeeld, [Host("domain.com", "*.domain.com")] komt overeen met domain.com, www.domain.com, en subdomain.domain.com.

De volgende code gebruikt RequireHost om de opgegeven host op de route te vereisen:

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

De volgende code gebruikt het [Host] kenmerk op de controller om een van de opgegeven hosts te vereisen:

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

Wanneer het [Host] kenmerk wordt toegepast op zowel de controller als de actiemethode:

  • Het kenmerk van de actie wordt gebruikt.
  • Het controllerkenmerk wordt genegeerd.

Richtlijnen voor prestaties voor routering

De meeste routering is bijgewerkt in ASP.NET Core 3.0 om de prestaties te verbeteren.

Wanneer een app prestatieproblemen heeft, wordt routering vaak vermoed als het probleem. De reden waarom routering wordt vermoed, is dat frameworks zoals controllers en Razor Pagina's de hoeveelheid tijd rapporteren die binnen het framework is besteed in hun logboekberichten. Wanneer er een aanzienlijk verschil is tussen de tijd die door controllers wordt gerapporteerd en de totale tijd van de aanvraag:

  • Ontwikkelaars elimineren hun app-code als de bron van het probleem.
  • Het is gebruikelijk om aan te nemen dat routering de oorzaak is.

Routering wordt getest met behulp van duizenden eindpunten. Het is onwaarschijnlijk dat een typische app een prestatieprobleem ondervindt door te groot te zijn. De meest voorkomende hoofdoorzaak van trage routeringsprestaties is meestal een slecht gedragende aangepaste middleware.

In dit volgende codevoorbeeld ziet u een basistechniek voor het beperken van de bron van vertraging:

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

Voor tijdsplanning van routering:

  • Interleave elke middleware met een kopie van de timing-middleware die in de code hierboven wordt weergegeven.
  • Voeg een unieke id toe om de timinggegevens te correleren met de code.

Dit is een eenvoudige manier om de vertraging te beperken wanneer deze aanzienlijk is, bijvoorbeeld meer dan 10ms. Het aftrekken van Time 2 van Time 1 geeft de tijd weer die binnen de UseRouting middleware is besteed.

De volgende code maakt gebruik van een compactere benadering van de voorgaande tijdscode:

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

Mogelijk dure routeringsfuncties

De volgende lijst biedt inzicht in routeringsfuncties die relatief duur zijn vergeleken met basisroutesjablonen:

  • Reguliere expressies: het is mogelijk om reguliere expressies te schrijven die complex zijn of langlopende tijd hebben met een kleine hoeveelheid invoer.
  • Complexe segmenten ({x}-{y}-{z}):
    • Zijn aanzienlijk duurder dan het parseren van een normaal URL-padsegment.
    • Resulteert in veel meer subtekenreeksen die toegewezen worden.
    • De complexe segmentlogica is niet bijgewerkt in de prestatieverbetering van de routering van ASP.NET Core 3.0.
  • Synchrone gegevenstoegang: veel complexe apps hebben databasetoegang als onderdeel van hun routering. ASP.NET Core 2.2 of eerdere routering biedt mogelijk niet de juiste uitbreidbaarheidspunten ter ondersteuning van routering van databasetoegang. Een voorbeeld is dat IRouteConstraint en IActionConstraint synchroon zijn. Uitbreidbaarheidspunten, zoals MatcherPolicy en EndpointSelectorContext zijn asynchroon.

Richtlijnen voor bibliotheekauteurs

Deze sectie bevat richtlijnen voor auteurs van bibliotheken die voortbouwen op routering. Deze details zijn bedoeld om ervoor te zorgen dat app-ontwikkelaars een goede ervaring hebben met het gebruik van bibliotheken en frameworks waarmee routering wordt uitgebreid.

Define endpoints

Als u een framework wilt maken dat gebruikmaakt van routing voor URL-koppeling, begint u met het definiëren van een gebruikerservaring die voortbouwt op UseEndpoints.

DO bovenop IEndpointRouteBuilder bouwen. Hierdoor kunnen gebruikers uw framework opstellen met andere ASP.NET Core-functies zonder verwarring. Elke ASP.NET Core-sjabloon bevat routering. Stel dat routering aanwezig is en bekend is voor gebruikers.

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...);

    endpoints.MapHealthChecks("/healthz");
});

DO retourneert een verzegeld betontype van een aanroep naar MapMyFramework(...) dat implementeert IEndpointConventionBuilder. De meeste frameworkmethoden Map... volgen dit patroon. De IEndpointConventionBuilder interface:

  • Hiermee staat u de composabiliteit van metagegevens toe.
  • Is gericht op verschillende uitbreidingsmethoden.

Door uw eigen type te declareren, kunt u uw eigen frameworkspecifieke functionaliteit toevoegen aan de opbouwfunctie. Het is prima om een door het framework gedeclareerde bouwer in te pakken en oproepen door te sturen.

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...).RequireAuthorization()
                                 .WithMyFrameworkFeature(awesome: true);

    endpoints.MapHealthChecks("/healthz");
});

OVERWEEG om je eigen EndpointDataSourcete schrijven. EndpointDataSource is het primitieve laag-niveau mechanisme voor het declareren en bijwerken van een verzameling eindpunten. EndpointDataSource is een krachtige API die wordt gebruikt door controllers en Razor Pages.

De routeringstests hebben een eenvoudig voorbeeld van een gegevensbron die niet wordt bijgewerkt.

PROBEER NIET standaard een EndpointDataSource registratie uit te voeren. Vereisen dat gebruikers uw framework registreren in UseEndpoints. De filosofie van routering is dat er standaard niets is inbegrepen en dat UseEndpoints is de plek om eindpunten te registreren.

Routerings-geïntegreerde middleware maken

OVERWEEG metagegevenstypen te definiëren als een interface.

DO maakt het mogelijk om metagegevenstypen te gebruiken als een kenmerk voor klassen en methoden.

public interface ICoolMetadata
{
    bool IsCool { get; }
}

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

Frameworks zoals controllers en Razor pagina's ondersteunen het toepassen van metagegevenskenmerken op typen en methoden. Als u metagegevenstypen declareert:

  • Maak ze toegankelijk als kenmerken.
  • De meeste gebruikers zijn bekend met het toepassen van kenmerken.

Door een metagegevenstype als interface te declareren, voegt u een andere flexibiliteitslaag toe:

  • Interfaces zijn samenstelbaar.
  • Ontwikkelaars kunnen hun eigen typen declareren die meerdere beleidsregels combineren.

DO maakt het mogelijk om metagegevens te overschrijven, zoals wordt weergegeven in het volgende voorbeeld:

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

De beste manier om deze richtlijnen te volgen, is om te voorkomen dat metagegevens van markeringen worden gedefinieerd:

  • Zoek niet alleen naar de aanwezigheid van een metagegevenstype.
  • Definieer een eigenschap op de metagegevens en controleer de eigenschap.

De verzameling metagegevens wordt gerangschikt en ondersteunt het overschrijven op prioriteit. In het geval van controllers zijn metagegevens voor de actiemethode het meest specifiek.

MAAK middleware nuttig met en zonder routering.

app.UseRouting();

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

app.UseEndpoints(endpoints =>
{
    // Your framework
    endpoints.MapMyFramework(...).RequireAuthorization();
});

Bekijk als voorbeeld van deze richtlijn de UseAuthorization middleware. Met de middleware voor autorisatie kunt u een terugvalbeleid doorgeven. Het terugvalbeleid, indien opgegeven, is van toepassing op beide:

  • Eindpunten zonder een opgegeven beleid.
  • Aanvragen die niet overeenkomen met een eindpunt.

Dit maakt de autorisatie-middleware nuttig buiten de context van routering. De autorisatie-middleware kan worden gebruikt voor traditionele middlewareprogrammering.

Debug diagnostics

Voor gedetailleerde routeringsdiagnose-uitvoer, stel Logging:LogLevel:Microsoft in op Debug. Stel in de ontwikkelomgeving het logboekniveau in:appsettings.Development.json

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