Delen via


Het routen naar controlleracties 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 artikelvoor de huidige release.

Warning

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor 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 artikelvoor de huidige release.

ASP.NET Core-controllers gebruiken de middleware routering om de URL's van binnenkomende aanvragen te vinden en deze toe te wijzen aan acties. Route templates:

  • Zijn gedefinieerd bij het opstarten in Program.cs of in kenmerken.
  • Beschrijven hoe URL-paden worden vergeleken met acties.
  • Worden gebruikt voor het genereren van URL's voor koppelingen. De gegenereerde koppelingen worden meestal geretourneerd in antwoorden.

Acties worden conventioneel gerouteerd of door kenmerken gerouteerd. Een route die op de controller of actie wordt geplaatst, is kenmerk-gerouteerd. Zie Gemengde routering voor meer informatie.

This document:

  • Hierin worden de interacties tussen MVC en routering uitgelegd:
    • Hoe typische MVC-apps gebruikmaken van routeringsfuncties.
    • Covers both:
    • Zie Routering voor geavanceerde routeringsdetails.
  • Verwijst naar het standaardrouteringssysteem met de naam eindpuntroutering. Het is mogelijk om controllers te gebruiken met de vorige versie van routering voor compatibiliteitsdoeleinden. Zie de migratiehandleiding 2.2-3.0 voor instructies.

Conventionele route instellen

De ASP.NET Core MVC-sjabloon genereert conventionele routeringscode die vergelijkbaar is met de volgende:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

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

app.Run();

MapControllerRoute wordt gebruikt om één route te maken. De enkele route heeft de naam default route. De meeste apps met controllers en weergaven gebruiken een routesjabloon die vergelijkbaar is met de default route. REST API's moeten kenmerkroutering gebruiken.

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

De routesjabloon "{controller=Home}/{action=Index}/{id?}":

  • Komt overeen met een URL-pad, zoals /Products/Details/5

  • Extraheert de routewaarden { controller = Products, action = Details, id = 5 } door het pad te tokeniseren. De extractie van routewaarden resulteert in een overeenkomst als de app een controller heeft met de naam ProductsController en een Details actie:

    public class ProductsController : Controller
    {
        public IActionResult Details(int id)
        {
            return ControllerContext.MyDisplayRouteInfo(id);
        }
    }
    

    MyDisplayRouteInfo wordt geleverd door het Rick.Docs.Samples.RouteInfo NuGet-pakket en toont routegegevens.

  • /Products/Details/5 model koppelt de waarde van id = 5 aan de id parameter om deze in te stellen op 5. Zie Modelbinding voor meer informatie.

  • {controller=Home} definieert Home als de standaardwaarde controller.

  • {action=Index} definieert Index als de standaardwaarde action.

  • Het ? teken in {id?} definieert id als optioneel.

    • Standaard- en optionele routeparameters hoeven niet aanwezig te zijn in het URL-pad voor een overeenstemming. Zie De naslaginformatie over routesjablonen voor een gedetailleerde beschrijving van de syntaxis van de routesjabloon.
  • Komt overeen met het URL-pad /.

  • Produceert de routewaarden { controller = Home, action = Index }.

De waarden voor controller en action maken gebruik van de standaardwaarden. id produceert geen waarde omdat er geen corresponderend segment in het URL-pad is. / komt alleen overeen als er een HomeController en Index actie bestaat:

public class HomeController : Controller
{
    public IActionResult Index() { ... }
}

Met behulp van de voorgaande controllerdefinitie en routesjabloon wordt de HomeController.Index actie uitgevoerd voor de volgende URL-paden:

  • /Home/Index/17
  • /Home/Index
  • /Home
  • /

Het URL-pad / maakt gebruik van de routesjabloon met standaardcontrollers Home en de actie Index. Het URL-pad /Home maakt gebruik van de standaardactie Index voor de routesjabloon.

De gemaksmethode MapDefaultControllerRoute:

app.MapDefaultControllerRoute();

Replaces:

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

Important

Routering wordt geconfigureerd met behulp van de UseRouting en UseEndpoints middleware. Controllers gebruiken:

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. Zie Routering in ASP.NET Corevoor meer informatie.

Conventional routing

Conventionele routering wordt gebruikt met controllers en weergaven. De default route:

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

Het voorgaande is een voorbeeld van een conventionele route. Dit wordt conventionele routering genoemd omdat er een conventie voor URL-paden wordt vastgesteld:

  • Het eerste padsegment, {controller=Home}wordt toegewezen aan de naam van de controller.
  • Het tweede segment, {action=Index}wordt toegewezen aan de naam van de actie.
  • Het derde segment {id?} wordt gebruikt voor een optioneel id. De ? in {id?} maakt het optioneel. id wordt gebruikt om toe te wijzen aan een modelentiteit.

Met deze default route wordt het URL-pad gebruikt:

  • /Products/List wordt toegewezen aan de ProductsController.List actie.
  • /Blog/Article/17 komt overeen met BlogController.Article en meestal wordt de id-parameter gekoppeld aan 17.

This mapping:

  • Gebaseerd op alleen de controller en actienamen.
  • Is niet gebaseerd op naamruimten, bronbestandslocaties of methodeparameters.

Met conventionele routering met de standaardroute kunt u de app maken zonder dat u voor elke actie een nieuw URL-patroon hoeft op te stellen. Voor een app met CRUD-stijlacties heeft u consistentie nodig voor de URL's over controllers heen:

  • Helpt de code te vereenvoudigen.
  • Maakt de gebruikersinterface voorspelbaarder.

Warning

De id in de voorgaande code wordt gedefinieerd als optioneel door de routesjabloon. Acties kunnen worden uitgevoerd zonder de optionele id die is opgegeven als onderdeel van de URL. Over het algemeen, wanneer id wordt weggelaten uit de URL:

  • id wordt door middel van modelbinding ingesteld op 0.
  • Er is geen entiteit gevonden in de overeenkomende id == 0database.

Kenmerkroutering biedt fijnmazige controle om de ID verplicht te stellen voor sommige acties, maar niet voor andere. De documentatie bevat standaard optionele parameters, zoals id wanneer ze waarschijnlijk in het juiste gebruik worden weergegeven.

De meeste apps moeten een eenvoudig en beschrijvend routeringsschema kiezen, zodat URL's leesbaar en zinvol zijn. De standaard conventionele route {controller=Home}/{action=Index}/{id?}:

  • Ondersteunt een eenvoudig en beschrijvend routeringsschema.
  • Is een nuttig startpunt voor apps op basis van de gebruikersinterface.
  • Is de enige routesjabloon die nodig is voor veel web-UI-apps. Voor grotere web-UI-apps is een andere route met het gebruik van Areas vaak alles wat nodig is.

MapControllerRoute en MapAreaRoute :

  • Wijs automatisch een orderwaarde toe aan hun eindpunten op basis van de volgorde die ze worden aangeroepen.

Eindpuntroutering in ASP.NET Core:

  • Heeft geen concept van routes.
  • Biedt geen bestelgaranties voor de uitvoering van uitbreidbaarheid, alle eindpunten worden in één keer verwerkt.

Schakel logging in om te zien hoe de ingebouwde routeringimplementaties verzoeken afhandelen, zoals Route.

Kenmerkroutering wordt verderop in dit document uitgelegd.

Meerdere conventionele routes

Meerdere conventionele routes kunnen worden geconfigureerd door meer aanroepen toe te voegen aan MapControllerRoute en MapAreaControllerRoute. Hiermee kunt u meerdere conventies definiëren of conventionele routes toevoegen die zijn toegewezen aan een specifieke actie, zoals:

app.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

De blog route in de voorgaande code is een toegewezen conventionele route. Dit wordt een speciale conventionele route genoemd omdat:

Omdat controller en action niet worden weergegeven in de routesjabloon "blog/{*article}" als parameters:

  • Ze kunnen alleen de standaardwaarden { controller = "Blog", action = "Article" }hebben.
  • Deze route wordt altijd toegewezen aan de actie BlogController.Article.

/Blog, /Blog/Articleen /Blog/{any-string} zijn de enige URL-paden die overeenkomen met de blogroute.

Het voorgaande voorbeeld:

  • De blog route heeft een hogere prioriteit bij het koppelen dan de default route, omdat deze eerst wordt toegevoegd.
  • Is een voorbeeld van slug-stijlroutering , waarbij het gebruikelijk is om een artikelnaam te hebben als onderdeel van de URL.

Warning

De routering in ASP.NET Core werkt niet als volgt:

  • Definieer een concept dat een route wordt genoemd. UseRouting voegt routekoppeling toe aan de middleware-pijplijn. De UseRouting middleware bekijkt de set eindpunten die in de app zijn gedefinieerd en selecteert de beste eindpuntovereenkomst op basis van de aanvraag.
  • Garanties bieden over de uitvoeringsvolgorde van uitbreidbaarheid, zoals IRouteConstraint of IActionConstraint.

Zie Routering voor referentiemateriaal over routering.

Conventionele routeringsvolgorde

Conventionele routering komt alleen overeen met een combinatie van actie en controller die door de app worden gedefinieerd. Dit is bedoeld om gevallen waarin conventionele routes elkaar overlappen te vereenvoudigen. Routes toevoegen met behulp van MapControllerRoute, MapDefaultControllerRoute en MapAreaControllerRoute wijst automatisch een orderwaarde toe aan hun eindpunten op basis van de volgorde waarin ze worden aangeroepen. Overeenkomsten van een route die eerder wordt weergegeven, hebben een hogere prioriteit. Conventionele routering is orderafhankelijk. In het algemeen moeten routes met gebieden eerder worden geplaatst omdat ze specifieker zijn dan routes zonder een gebied. Toegewezen conventionele routes met catch-all routeparameters, zoals {*article} kunnen een route te greedy maken, wat betekent dat deze overeenkomt met URL's die u hebt bedoeld om te worden vergeleken met andere routes. Plaats de gretige routes later in de routetabel om gretige overeenkomsten te voorkomen.

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.

Ambigu acties oplossen

Wanneer twee eindpunten overeenkomen via routering, moet routering een van de volgende handelingen uitvoeren:

  • Kies de beste kandidaat.
  • Gooi een uitzondering.

For example:

public class Products33Controller : Controller
{
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpPost]
    public IActionResult Edit(int id, Product product)
    {
        return ControllerContext.MyDisplayRouteInfo(id, product.name);
    }
}

De voorgaande controller definieert twee acties die overeenkomen met:

  • Het URL-pad /Products33/Edit/17
  • Routegegevens { controller = Products33, action = Edit, id = 17 }.

Dit is een typisch patroon voor MVC-controllers:

  • Edit(int) geeft een formulier weer om een product te bewerken.
  • Edit(int, Product) verwerkt het geplaatste formulier.

De juiste route oplossen:

  • Edit(int, Product) is geselecteerd wanneer de aanvraag een HTTP POSTis.
  • Edit(int) is geselecteerd wanneer het HTTP-werkwoord iets anders is. Edit(int) wordt over het algemeen via GET.

De HttpPostAttribute, [HttpPost]wordt verstrekt aan routering, zodat deze kan kiezen op basis van de HTTP-methode van de aanvraag. De HttpPostAttribute maakt Edit(int, Product) een betere match dan Edit(int).

Het is belangrijk om inzicht te hebben in de rol van kenmerken zoals HttpPostAttribute. Vergelijkbare kenmerken worden gedefinieerd voor andere HTTP-woorden. Bij conventionele routering is het gebruikelijk dat acties dezelfde actienaam gebruiken wanneer ze deel uitmaken van een toonformulier of het indienen van een formulierworkflow. Zie bijvoorbeeld de twee bewerkingsactiemethoden onderzoeken.

Als routering geen beste kandidaat kan kiezen, wordt er een AmbiguousMatchException gegenereerd, waarin de meerdere overeenkomende eindpunten worden vermeld.

Conventionele routenamen

De tekenreeksen "blog" en "default" in de volgende voorbeelden zijn conventionele routenamen:

app.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

De routenamen geven de route een logische naam. De benoemde route kan worden gebruikt voor het genereren van URL's. Het gebruik van een benoemde route vereenvoudigt het maken van URL's wanneer de volgorde van routes ingewikkeld kan maken voor het genereren van URL's. Routenamen moeten uniek zijn voor de hele toepassing.

Route names:

  • Geen invloed hebben op URL-matchen of verzoekafhandeling.
  • Worden alleen gebruikt voor het genereren van URL's.

Het concept van de routenaam wordt weergegeven in routering als IEndpointNameMetadata. De termen routenaam en eindpuntnaam:

  • Are interchangeable.
  • Welke wordt gebruikt in documentatie en code, is afhankelijk van de API die wordt beschreven.

Kenmerkroutering voor REST API's

REST API's moeten kenmerkroutering gebruiken om de functionaliteit van de app te modelleren als een set resources waarin bewerkingen worden vertegenwoordigd door HTTP-woorden.

Kenmerkroutering maakt gebruik van een set kenmerken om acties rechtstreeks toe te wijzen aan routesjablonen. De volgende code is typisch voor een REST API en wordt gebruikt in het volgende voorbeeld:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

In de voorgaande code wordt MapControllers aangeroepen om kenmerkgerouteerde controllers toe te wijzen.

In het volgende voorbeeld:

  • HomeController komt overeen met een set URL's die vergelijkbaar zijn met de standaardconventionele route {controller=Home}/{action=Index}/{id?} .
public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

De HomeController.Index actie wordt uitgevoerd voor een van de URL-paden/, /Homeof /Home/Index/Home/Index/3.

In dit voorbeeld wordt een belangrijk programmeerverschil tussen kenmerkroutering en conventionele routering gemarkeerd. Voor kenmerkroutering is meer invoer vereist om een route op te geven. De conventionele standaardroute verwerkt routes beknopter. Met kenmerkroutering kunt u echter nauwkeurig bepalen welke routesjablonen van toepassing zijn op elke actie.

Bij kenmerkroutering spelen de controller- en actienamen geen rol waarin actie wordt vergeleken, tenzij tokenvervanging wordt gebruikt. Het volgende voorbeeld komt overeen met dezelfde URL's als in het vorige voorbeeld:

public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

De volgende code maakt gebruik van tokenvervanging voor action en controller:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("[controller]/[action]")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("[controller]/[action]")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

De volgende code is van toepassing op [Route("[controller]/[action]")] de controller:

[Route("[controller]/[action]")]
public class HomeController : Controller
{
    [Route("~/")]
    [Route("/Home")]
    [Route("~/Home/Index")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

In de voorgaande code moeten de Index methodesjablonen voorafgegaan worden door / of ~/ bij de routesjablonen. Routesjablonen die worden toegepast op een actie die begint met / of ~/ niet worden gecombineerd met routesjablonen die zijn toegepast op de controller.

Zie de prioriteit van routesjablonen voor informatie over de selectie van routesjablonen.

Gereserveerde routeringsnamen

De volgende trefwoorden zijn gereserveerde routeparameternamen bij het gebruik van Controllers of Razor Pages:

  • action
  • area
  • controller
  • handler
  • page

Het gebruik page als routeparameter met kenmerkroutering is een veelvoorkomende fout. Dit leidt tot inconsistent en verwarrend gedrag met het genereren van URL's.

public class MyDemo2Controller : Controller
{
    [Route("/articles/{page}")]
    public IActionResult ListArticles(int page)
    {
        return ControllerContext.MyDisplayRouteInfo(page);
    }
}

De speciale parameternamen worden door de URL-generatie gebruikt om te bepalen of een URL-generatiebewerking verwijst naar een Razor pagina of naar een controller.

De volgende trefwoorden zijn gereserveerd in de context van een Razor weergave of een Razor pagina:

  • page
  • using
  • namespace
  • inject
  • section
  • inherits
  • model
  • addTagHelper
  • removeTagHelper

Deze trefwoorden mogen niet worden gebruikt voor koppelingsgeneraties, modelgebonden parameters of eigenschappen op het hoogste niveau.

HTTP-werkwoordsjablonen

ASP.NET Core heeft de volgende HTTP-werkwoordsjablonen:

Route templates

ASP.NET Core heeft de volgende routesjablonen:

Kenmerkroutering met Http-werkwoordkenmerken

Houd rekening met de volgende controller:

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

In de voorgaande code:

  • Elke actie bevat het [HttpGet] kenmerk, dat alleen overeenkomt met HTTP GET-aanvragen.
  • De GetProduct actie bevat de "{id}" sjabloon en wordt daarom id toegevoegd aan de "api/[controller]" sjabloon op de controller. De sjabloon voor methoden is "api/[controller]/{id}". Daarom komt deze actie alleen overeen met GET-aanvragen voor het formulier/api/test2/xyz/api/test2/123,/api/test2/{any string} enzovoort.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • De GetIntProduct actie bevat de "int/{id:int}" sjabloon. Het :int gedeelte van de sjabloon beperkt de id routewaarden tot tekenreeksen die kunnen worden geconverteerd naar een geheel getal. Een GET-aanvraag voor /api/test2/int/abc:
    • Komt niet overeen met deze actie.
    • Retourneert een fout 404 Niet Gevonden.
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • De GetInt2Product actie bevat {id} in de sjabloon, maar legt geen beperkingen op aan id tot waarden die kunnen worden geconverteerd naar een geheel getal. Een GET-aanvraag voor /api/test2/int2/abc:
    • Komt overeen met deze route.
    • Modelbinding kan abc niet omzetten naar een geheel getal. De id parameter van de methode is geheel getal.
    • Retourneert een 400 Bad Request omdat het niet gelukt is om abc naar een geheel getal te converteren.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

Kenmerkroutering kan gebruikmaken van HttpMethodAttribute kenmerken zoals HttpPostAttribute, HttpPutAttributeen HttpDeleteAttribute. Alle HTTP-werkwoordkenmerken accepteren een routesjabloon. In het volgende voorbeeld ziet u twee acties die overeenkomen met dezelfde routesjabloon:

[ApiController]
public class MyProductsController : ControllerBase
{
    [HttpGet("/products3")]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpPost("/products3")]
    public IActionResult CreateProduct(MyProduct myProduct)
    {
        return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
    }
}

Het URL-pad gebruiken /products3:

  • De MyProductsController.ListProducts-actie wordt uitgevoerd wanneer het HTTP-werkwoordGET is.
  • De MyProductsController.CreateProduct-actie wordt uitgevoerd wanneer het HTTP-werkwoordPOST is.

Bij het bouwen van een REST API is het zeldzaam dat u moet gebruiken [Route(...)] voor een actiemethode, omdat de actie alle HTTP-methoden accepteert. Het is beter om het specifiekere HTTP-werkwoordkenmerk te gebruiken om precies te zijn wat uw API ondersteunt. Clients van REST API's zullen naar verwachting weten welke paden en HTTP-werkwoorden zijn toegewezen aan specifieke logische bewerkingen.

REST API's moeten kenmerkroutering gebruiken om de functionaliteit van de app te modelleren als een set resources waarin bewerkingen worden vertegenwoordigd door HTTP-woorden. Dit betekent dat veel bewerkingen, bijvoorbeeld GET en POST op dezelfde logische resource, dezelfde URL gebruiken. Kenmerkroutering biedt een beheerniveau dat nodig is om de indeling van een openbaar eindpunt van een API zorgvuldig te ontwerpen.

Omdat een kenmerkroute van toepassing is op een specifieke actie, is het eenvoudig om parameters vereist te maken als onderdeel van de definitie van de routesjabloon. In het volgende voorbeeld id is dit vereist als onderdeel van het URL-pad:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

De Products2ApiController.GetProduct(int) actie:

  • Wordt uitgevoerd met een url-pad zoals /products2/3
  • Wordt niet uitgevoerd met het URL-pad /products2.

Met het kenmerk [Verbruiken] kan een actie de ondersteunde inhoudstypen voor aanvragen beperken. Zie Ondersteunde aanvraaginhoudstypen definiëren met het kenmerk Verbruiken voor meer informatie.

Zie Routering voor een volledige beschrijving van routesjablonen en gerelateerde opties.

Zie [ApiController] voor meer informatie.

Route name

De volgende code definieert een routenaam van Products_List:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Routenamen kunnen worden gebruikt om een URL te genereren op basis van een specifieke route. Route names:

  • Dit heeft geen invloed op het URL-overeenkomende gedrag van routering.
  • Worden alleen gebruikt voor het genereren van URL's.

Routenamen moeten uniek zijn voor de hele toepassing.

Vergelijk de voorgaande code met de conventionele standaardroute, die de id parameter definieert als optioneel ({id?}). De mogelijkheid om API's nauwkeurig op te geven heeft voordelen, zoals het toestaan dat /products en /products/5 worden uitgevoerd door verschillende acties.

Het combineren van kenmerkroutes

Om kenmerkroutering minder herhalend te maken, worden routekenmerken op de controller gecombineerd met routekenmerken voor de afzonderlijke acties. Routesjablonen die op de controller zijn gedefinieerd, worden toegevoegd aan de routesjablonen van de acties. Als u een routekenmerk op de controller plaatst, worden alle acties in de controller gebruikt om kenmerkroutering te gebruiken.

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
    [HttpGet]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

In het voorgaande voorbeeld:

  • Het URL-pad /products kan overeenkomen met ProductsApi.ListProducts
  • Het URL-pad /products/5 kan overeenkomen met ProductsApi.GetProduct(int).

Beide acties komen alleen overeen met HTTP GET omdat ze zijn gemarkeerd met het [HttpGet] kenmerk.

Routesjablonen die worden toegepast op een actie die begint met / of ~/ niet worden gecombineerd met routesjablonen die zijn toegepast op de controller. Het volgende voorbeeld komt overeen met een set URL-paden die vergelijkbaar zijn met de standaardroute.

[Route("Home")]
public class HomeController : Controller
{
    [Route("")]
    [Route("Index")]
    [Route("/")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("About")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

In de volgende tabel worden de [Route] kenmerken in de voorgaande code uitgelegd:

Attribute Combineert met [Route("Home")] Definieert routesjabloon
[Route("")] Yes "Home"
[Route("Index")] Yes "Home/Index"
[Route("/")] No ""
[Route("About")] Yes "Home/About"

Routevolgorde kenmerk

Routering bouwt een structuur en komt overeen met alle eindpunten tegelijk:

  • De routevermeldingen gedragen zich alsof ze in een ideale volgorde worden geplaatst.
  • De meest specifieke routes hebben de kans om uit te voeren vóór de meer algemene routes.

Een kenmerkroute zoals blog/search/{topic} is bijvoorbeeld specifieker dan een kenmerkroute zoals blog/{*article}. De blog/search/{topic} route heeft standaard een hogere prioriteit, omdat deze specifieker is. Met conventionele routering is de ontwikkelaar verantwoordelijk voor het plaatsen van routes in de gewenste volgorde.

Kenmerkroutes kunnen een order configureren met behulp van de Order eigenschap. Alle door het framework geleverde routekenmerken omvatten Order . Routes worden verwerkt op basis van een oplopend type eigenschap Order . De standaardvolgorde is 0. Een route instellen met behulp van Order = -1 uitvoeringen voor routes die geen order instellen. Een route instellen met behulp van Order = 1 vindt plaats na de standaard routevolgorde.

Vermijd afhankelijk Ordervan . Als de URL-ruimte van een app expliciete volgordewaarden vereist om correct te routeren, is het waarschijnlijk ook verwarrend voor clients. Over het algemeen selecteert kenmerkroutering de juiste route met URL-overeenkomsten. Als de standaardvolgorde die wordt gebruikt voor het genereren van URL's niet werkt, is het gebruik van een routenaam als onderdrukking meestal eenvoudiger dan het toepassen van de Order eigenschap.

Houd rekening met de volgende twee controllers die beide de routekoppeling /homedefiniëren:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Als u aanvraagt /home met de voorgaande code, wordt er een uitzondering gegenereerd die vergelijkbaar is met de volgende:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

 WebMvcRouting.Controllers.HomeController.Index
 WebMvcRouting.Controllers.MyDemoController.MyIndex

Als u een van de routekenmerken toevoegt Order , wordt de dubbelzinnigheid opgelost:

[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
    return ControllerContext.MyDisplayRouteInfo();
}

Met de voorgaande code /home wordt het HomeController.Index eindpunt uitgevoerd. Om naar de MyDemoController.MyIndex, aanvraag /home/MyIndexte gaan . Note:

  • De voorgaande code is een voorbeeld of een slecht routeringsontwerp. Deze is gebruikt om de Order eigenschap te illustreren.
  • De Order eigenschap lost alleen de dubbelzinnigheid op, die sjabloon kan niet worden vergeleken. Het is beter om de [Route("Home")] sjabloon te verwijderen.

Zie Razor route- en app-conventies voor pagina's: Routevolgorde voor informatie over routevolgordes met Razor Pagina's.

In sommige gevallen wordt een HTTP 500-fout geretourneerd met ambigu routes. Gebruik loggen om te zien welke eindpunten de AmbiguousMatchExceptionveroorzaken.

Tokenvervanging in routesjablonen [controller], [actie], [gebied]

Voor het gemak ondersteunen kenmerkroutes tokenvervanging door een token tussen vierkante haken ([, ]) te sluiten. De tokens [action], [area]en [controller] worden vervangen door de waarden van de actienaam, gebiedsnaam en controllernaam uit de actie waarin de route is gedefinieerd:

[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
    [HttpGet]
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }


    [HttpGet("{id}")]
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

In de voorgaande code:

[HttpGet]
public IActionResult List()
{
    return ControllerContext.MyDisplayRouteInfo();
}
  • Wedstrijden /Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
    return ControllerContext.MyDisplayRouteInfo(id);
}
  • Wedstrijden /Products0/Edit/{id}

Tokenvervanging vindt plaats als de laatste stap van het bouwen van de attribuutroutes. Het voorgaande voorbeeld gedraagt zich hetzelfde als de volgende code:

public class Products20Controller : Controller
{
    [HttpGet("[controller]/[action]")]  // Matches '/Products20/List'
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("[controller]/[action]/{id}")]   // Matches '/Products20/Edit/{id}'
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Als u dit leest in een andere taal dan Engels, laat het ons dan weten in dit GitHub-discussieprobleem als u de codeopmerkingen in uw eigen taal wilt zien.

Attribuutsroutes kunnen ook worden gecombineerd met overerving. De kracht hiervan komt goed tot uiting wanneer het wordt gecombineerd met tokenvervanging. Tokenvervanging is ook van toepassing op routenamen die zijn gedefinieerd door kenmerkroutes. [Route("[controller]/[action]", Name="[controller]_[action]")]genereert een unieke routenaam voor elke actie:

[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}

public class Products11Controller : MyBase2Controller
{
    [HttpGet]                      // /api/products11/list
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]             //    /api/products11/edit/3
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Als u het letterlijke scheidingsteken voor tokenvervanging [ wilt vergelijken of ], escapet u het door het teken ([[ of ]]) te herhalen.

Een parametertransformatie gebruiken om tokenvervanging aan te passen

Tokenvervanging kan worden aangepast met behulp van een parametertransformator. Een parametertransformator implementeert IOutboundParameterTransformer en transformeert de waarde van parameters. Een aangepaste SlugifyParameterTransformer parametertransformator wijzigt bijvoorbeeld de SubscriptionManagement routewaarde in subscription-management:

using System.Text.RegularExpressions;

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

Dit RouteTokenTransformerConvention is een toepassingsmodelconventie die:

  • Hiermee past u een parametertransformatie toe op alle kenmerkroutes in een toepassing.
  • Hiermee worden de waarden van het kenmerkroutetoken aangepast wanneer ze worden vervangen.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

De voorgaande ListAll methode komt overeen /subscription-management/list-all.

De RouteTokenTransformerConvention optie is geregistreerd als een optie:

using Microsoft.AspNetCore.Mvc.ApplicationModels;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews(options =>
{
    options.Conventions.Add(new RouteTokenTransformerConvention(
                                 new SlugifyParameterTransformer()));
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

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

app.Run();

Zie MDN-webdocumenten op Slug voor de definitie van Slug.

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 kenmerkroutes

Kenmerkroutering ondersteunt het definiëren van meerdere routes die dezelfde actie bereiken. Het meest voorkomende gebruik hiervan is om het gedrag van de standaard conventionele route na te bootsen, zoals wordt weergegeven in het volgende voorbeeld:

[Route("[controller]")]
public class Products13Controller : Controller
{
    [Route("")]     // Matches 'Products13'
    [Route("Index")] // Matches 'Products13/Index'
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

Als u meerdere routekenmerken op de controller plaatst, betekent dit dat elke eigenschap wordt gecombineerd met elk van de routekenmerken op de actiemethoden:

[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
    [HttpPost("Buy")]       // Matches 'Products6/Buy' and 'Store/Buy'
    [HttpPost("Checkout")]  // Matches 'Products6/Checkout' and 'Store/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Alle http-bewerkingsroutebeperkingen worden geïmplementeerd IActionConstraint.

Wanneer meerdere routekenmerken die worden geïmplementeerd IActionConstraint op een actie worden geplaatst:

  • Elke actiebeperking combineert met de routesjabloon die is toegepast op de controller.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
    [HttpPut("Buy")]        // Matches PUT 'api/Products7/Buy'
    [HttpPost("Checkout")]  // Matches POST 'api/Products7/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Het gebruik van meerdere routes voor acties kan nuttig en krachtig lijken, het is beter om de URL-ruimte van uw app eenvoudig en goed gedefinieerd te houden. Gebruik meerdere routes voor acties alleen wanneer dat nodig is, bijvoorbeeld om bestaande clients te ondersteunen.

Optionele parameters, standaardwaarden en beperkingen voor kenmerkroute opgeven

Kenmerkroutes ondersteunen dezelfde inlinesyntaxis als conventionele routes om optionele parameters, standaardwaarden en beperkingen op te geven.

public class Products14Controller : Controller
{
    [HttpPost("product14/{id:int}")]
    public IActionResult ShowProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

In de voorgaande code [HttpPost("product14/{id:int}")] past u een routebeperking toe. De Products14Controller.ShowProduct-actie komt alleen overeen met URL-paden zoals /product14/3. Het gedeelte {id:int} van de routesjabloon beperkt dat segment tot alleen gehele getallen.

Zie De naslaginformatie over routesjablonen voor een gedetailleerde beschrijving van de syntaxis van de routesjabloon.

Aangepaste routekenmerken met IRouteTemplateProvider

Alle routekenmerken worden geïmplementeerd IRouteTemplateProvider. De ASP.NET Core-runtime:

  • Zoekt naar kenmerken in controllerklassen en actiemethoden wanneer de app wordt gestart.
  • Maakt gebruik van de kenmerken die worden geïmplementeerd IRouteTemplateProvider om de eerste set routes te bouwen.

Implementeren IRouteTemplateProvider om aangepaste routekenmerken te definiëren. Met elke IRouteTemplateProvider route kunt u één route definiëren met een aangepaste routesjabloon, volgorde en naam:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
    public string Template => "api/[controller]";
    public int? Order => 2;
    public string Name { get; set; } = string.Empty;
}

[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
    // GET /api/MyTestApi
    [HttpGet]
    public IActionResult Get()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

De voorgaande Get methode retourneert Order = 2, Template = api/MyTestApi.

Toepassingsmodel gebruiken om kenmerkroutes aan te passen

Het toepassingsmodel:

  • Is een objectmodel dat is gemaakt bij het opstarten in Program.cs.
  • Bevat alle metagegevens die door ASP.NET Core worden gebruikt om de acties in een app te routeren en uit te voeren.

Het toepassingsmodel bevat alle gegevens die zijn verzameld uit routekenmerken. De gegevens van routekenmerken worden geleverd door de IRouteTemplateProvider implementatie. Conventions:

  • Kan worden geschreven om het toepassingsmodel te wijzigen om aan te passen hoe routering zich gedraagt.
  • Worden gelezen bij het opstarten van de app.

In deze sectie ziet u een eenvoudig voorbeeld van het aanpassen van routering met behulp van een toepassingsmodel. Met de volgende code worden routes grofweg afgestemd op de mapstructuur van het project.

public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
    private readonly string _baseNamespace;

    public NamespaceRoutingConvention(string baseNamespace)
    {
        _baseNamespace = baseNamespace;
    }

    public void Apply(ControllerModel controller)
    {
        var hasRouteAttributes = controller.Selectors.Any(selector =>
                                                selector.AttributeRouteModel != null);
        if (hasRouteAttributes)
        {
            return;
        }

        var namespc = controller.ControllerType.Namespace;
        if (namespc == null)
            return;
        var template = new StringBuilder();
        template.Append(namespc, _baseNamespace.Length + 1,
                        namespc.Length - _baseNamespace.Length - 1);
        template.Replace('.', '/');
        template.Append("/[controller]/[action]/{id?}");

        foreach (var selector in controller.Selectors)
        {
            selector.AttributeRouteModel = new AttributeRouteModel()
            {
                Template = template.ToString()
            };
        }
    }
}

Met de volgende code voorkomt u dat de namespace conventie wordt toegepast op controllers die door een kenmerk worden gerouteerd:

public void Apply(ControllerModel controller)
{
    var hasRouteAttributes = controller.Selectors.Any(selector =>
                                            selector.AttributeRouteModel != null);
    if (hasRouteAttributes)
    {
        return;
    }

De volgende controller gebruikt NamespaceRoutingConventionbijvoorbeeld niet:

[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
    // /managers/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        return Content($"Index- template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

De methode NamespaceRoutingConvention.Apply:

  • Doet niets als de controller wordt gerouteerd.
  • Hiermee stelt u de sjabloon controllers in op basis van de namespacesjabloon , waarbij de basis namespace is verwijderd.

De NamespaceRoutingConvention kan worden toegepast in Program.cs:

using My.Application.Controllers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews(options =>
{
    options.Conventions.Add(
     new NamespaceRoutingConvention(typeof(HomeController).Namespace!));
});

var app = builder.Build();

Denk bijvoorbeeld aan de volgende controller:

using Microsoft.AspNetCore.Mvc;

namespace My.Application.Admin.Controllers
{
    public class UsersController : Controller
    {
        // GET /admin/controllers/users/index
        public IActionResult Index()
        {
            var fullname = typeof(UsersController).FullName;
            var template = 
                ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
            var path = Request.Path.Value;

            return Content($"Path: {path} fullname: {fullname}  template:{template}");
        }

        public IActionResult List(int? id)
        {
            var path = Request.Path.Value;
            return Content($"Path: {path} ID:{id}");
        }
    }
}

In de voorgaande code:

  • De basis namespace is My.Application.
  • De volledige naam van de voorgaande controller is My.Application.Admin.Controllers.UsersController.
  • Hiermee NamespaceRoutingConvention stelt u de controllersjabloon in op Admin/Controllers/Users/[action]/{id?.

De NamespaceRoutingConvention kan ook worden toegepast als een kenmerk op een controller:

[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
    // /admin/controllers/test/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        var actionname = ControllerContext.ActionDescriptor.ActionName;
        return Content($"Action- {actionname} template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

Gemengde routering: Kenmerkroutering versus conventionele routering

ASP.NET Core-apps kunnen het gebruik van conventionele routering en kenmerkroutering combineren. Het is gebruikelijk om conventionele routes te gebruiken voor controllers die HTML-pagina's voor browsers bedienen en kenmerkroutering voor controllers die API's bedienen REST .

Acties worden of conventioneel gerouteerd of via attributen gerouteerd. Wanneer je een route op de controller of de actie plaatst, wordt deze met attributen gerouteerd. Acties die kenmerkroutes definiëren, kunnen niet worden bereikt via de conventionele routes en omgekeerd. Elk route-attribuut op de controller zorgt ervoor dat alle acties in de controller worden gerouteerd.

Kenmerkroutering en conventionele routering gebruiken dezelfde routeringsengine.

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;

URL-generatie en omgevingswaarden

Apps kunnen functies voor het genereren van routerings-URL's gebruiken om URL-koppelingen naar acties te genereren. Het genereren van URL's elimineert hardcoderings-URL's , waardoor code robuuster en onderhoudbaarer wordt. Deze sectie is gericht op de functies voor het genereren van URL's die door MVC worden geboden en beslaan alleen de basisbeginselen van het genereren van URL's. Zie Routering voor een gedetailleerde beschrijving van het genereren van URL's.

De IUrlHelper interface is het onderliggende element van de infrastructuur tussen MVC en routering voor het genereren van URL's. Een exemplaar van IUrlHelper is beschikbaar via de Url eigenschap in controllers, weergaven en weergaveonderdelen.

In het volgende voorbeeld wordt de IUrlHelper interface gebruikt via de Controller.Url eigenschap om een URL naar een andere actie te genereren.

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Als de app gebruikmaakt van de standaard conventionele route, is de waarde van de url variabele de URL-padtekenreeks /UrlGeneration/Destination. Dit URL-pad wordt gemaakt door routering door het volgende te combineren:

  • De routewaarden van de huidige aanvraag, die omgevingswaarden worden genoemd.
  • De waarden die worden doorgegeven aan Url.Action en die waarden invoegen in de routesjabloon:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

Elke routeparameter in de routesjabloon wordt vervangen door namen die overeenkomen met de waarden en omgevingswaarden. Een routeparameter die geen waarde heeft, kan:

  • Gebruik een standaardwaarde als deze een waarde heeft.
  • Overgeslagen als dit optioneel is. Bijvoorbeeld de id van de routesjabloon {controller}/{action}/{id?}.

Het genereren van url's mislukt als een vereiste routeparameter geen bijbehorende waarde heeft. Als het genereren van url's voor een route mislukt, wordt de volgende route geprobeerd totdat alle routes zijn geprobeerd of een overeenkomst wordt gevonden.

In het voorgaande voorbeeld wordt uitgegaan van Url.Actionconventionele routering. Het genereren van URL's werkt op dezelfde manier met kenmerkroutering, hoewel de concepten verschillen. Bij gebruik van conventionele routering:

  • De routewaarden worden gebruikt om een sjabloon uit te vouwen.
  • De routewaarden voor controller en action worden meestal weergegeven in die sjabloon. Dit werkt omdat de URL's die overeenkomen met routering voldoen aan een conventie.

In het volgende voorbeeld wordt kenmerkroutering gebruikt:

public class UrlGenerationAttrController : Controller
{
    [HttpGet("custom")]
    public IActionResult Source()
    {
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination")]
    public IActionResult Destination()
    {
       return ControllerContext.MyDisplayRouteInfo();
    }
}

De Source actie in de voorgaande code genereert custom/url/to/destination.

LinkGenerator is toegevoegd in ASP.NET Core 3.0 als alternatief voor IUrlHelper. LinkGenerator biedt vergelijkbare maar flexibelere functionaliteit. Elke methode op IUrlHelper heeft ook een bijbehorende reeks methoden LinkGenerator .

URL's genereren op actienaam

Url.Action, LinkGenerator.GetPathByAction en alle gerelateerde overbelastingen zijn ontworpen om het doeleindpunt te genereren door een controllernaam en actienaam op te geven.

Wanneer u Url.Action gebruikt, worden de huidige routewaarden voor controller en action geleverd door de runtime:

  • De waarde van controller en action maken deel uit van zowel omgevingswaarden als waarden. De methode Url.Action gebruikt altijd de huidige waarden van action en controller genereert een URL-pad dat naar de huidige actie routeert.

Routering probeert de waarden in omgevingswaarden te gebruiken om informatie in te vullen die niet is opgegeven bij het genereren van een URL. Overweeg een route zoals {a}/{b}/{c}/{d} met omgevingswaarden { a = Alice, b = Bob, c = Carol, d = David }:

  • Routering bevat voldoende informatie om een URL te genereren zonder extra waarden.
  • Routering heeft voldoende informatie omdat alle routeparameters een waarde hebben.

Als de waarde { d = Donovan } wordt toegevoegd:

  • De waarde { d = David } wordt genegeerd.
  • Het gegenereerde URL-pad is Alice/Bob/Carol/Donovan.

Waarschuwing: URL-paden zijn hiërarchisch. Als in het voorgaande voorbeeld de waarde { c = Cheryl } wordt toegevoegd:

  • Beide waarden { c = Carol, d = David } worden genegeerd.
  • Er is geen waarde meer voor d en het genereren van URL's mislukt.
  • De gewenste waarden van c en d moeten worden opgegeven om een URL te genereren.

U kunt verwachten dat dit probleem zich voordoet met de standaardroute {controller}/{action}/{id?}. Dit probleem komt zelden voor in de praktijk omdat Url.Action altijd expliciet een controller- en action-waarde specificeert.

Verschillende overbelastingen van URL.Action voeren een routewaardenobject uit om waarden op te geven voor andere routeparameters dan controller en action. Het object voor routewaarden wordt vaak gebruikt met id. Bijvoorbeeld: Url.Action("Buy", "Products", new { id = 17 }). Het routewaardenobject:

  • Volgens conventie is een object meestal van anoniem type.
  • Kan een IDictionary<> of een POCO zijn).

Eventuele extra routewaarden die niet overeenkomen met routeparameters, worden in de querytekenreeks geplaatst.

public IActionResult Index()
{
    var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
    return Content(url!);
}

De voorgaande code genereert /Products/Buy/17?color=red.

Met de volgende code wordt een absolute URL gegenereerd:

public IActionResult Index2()
{
    var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
    // Returns https://localhost:5001/Products/Buy/17
    return Content(url!);
}

Gebruik een van de volgende manieren om een absolute URL te maken:

  • Een overload-functie die een protocol accepteert. Bijvoorbeeld de voorgaande code.
  • LinkGenerator.GetUriByAction, waarmee standaard absolute URI's worden gegenereerd.

URLs per route genereren

In de voorgaande code is het genereren van een URL gedemonstreerd door de controller en de actienaam door te geven. IUrlHelper biedt ook de url.RouteUrl-reeks methoden. Deze methoden zijn vergelijkbaar met Url.Action, maar ze kopiëren niet de huidige waarden van action en controller naar de routewaarden. Het meest voorkomende gebruik van Url.RouteUrl:

  • Hiermee geeft u een routenaam op voor het genereren van de URL.
  • Over het algemeen specificeer je geen controller- of actienaam.
public class UrlGeneration2Controller : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.RouteUrl("Destination_Route");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

In het volgende Razor bestand wordt een HTML-koppeling naar het Destination_Routevolgende bestand gegenereerd:

<h1>Test Links</h1>

<ul>
    <li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>

URLs genereren in HTML en Razor

IHtmlHelperbiedt de HtmlHelper methoden Html.BeginForm en Html.ActionLink om respectievelijk elementen te genereren<form>.<a> Deze methoden gebruiken de methode Url.Action om een URL te genereren en ze accepteren vergelijkbare argumenten. De metgezellen voor Url.RouteUrl zijn HtmlHelper en Html.BeginRouteForm, die vergelijkbare functionaliteit hebben.

TagHelpers genereren URL's met behulp van de TagHelper form en de TagHelper <a>. Beide gebruiken IUrlHelper voor hun implementatie. Zie Tag Helpers in formulieren voor meer informatie.

In weergaven is de IUrlHelper functie beschikbaar via de Url eigenschap voor elke ad-hoc-URL-generatie die niet wordt gedekt door het bovenstaande.

URL-generatie in actieresultaten

In de voorgaande voorbeelden is het gebruik IUrlHelper in een controller getoond. Het meest voorkomende gebruik in een controller is het genereren van een URL als onderdeel van een actieresultaat.

De ControllerBase en Controller basisklassen bieden handige methoden voor actieresultaten die verwijzen naar een andere actie. Een typisch gebruik is om om te leiden na het accepteren van gebruikersinvoer:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
    if (ModelState.IsValid)
    {
        // Update DB with new details.
        ViewData["Message"] = $"Successful edit of customer {id}";
        return RedirectToAction("Index");
    }
    return View(customer);
}

De actie resulteert in factory-methoden zoals RedirectToAction en CreatedAtAction volgen een vergelijkbaar patroon als de methoden op IUrlHelper.

Speciaal geval voor speciale conventionele routes

Conventionele routering kan een speciaal soort routedefinitie gebruiken, een speciale conventionele route genoemd. In het volgende voorbeeld is de benoemde blog route een toegewezen conventionele route:

app.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
               pattern: "{controller=Home}/{action=Index}/{id?}");

Met behulp van de voorgaande routedefinities Url.Action("Index", "Home") genereert u het URL-pad / met behulp van de default route, maar waarom? U kunt raden dat de routewaarden { controller = Home, action = Index } voldoende zijn om een URL te genereren met behulp van blog, en het resultaat zou zijn /blog?action=Index&controller=Home.

Toegewezen conventionele routes zijn afhankelijk van een speciaal gedrag van standaardwaarden die geen bijbehorende routeparameter hebben die voorkomt dat de route te greedy is met het genereren van URL's. In dit geval zijn de standaardwaarden en { controller = Blog, action = Article }worden ze controller niet action weergegeven als routeparameter. Wanneer routering URL-generatie uitvoert, moeten de opgegeven waarden overeenkomen met de standaardwaarden. Het genereren van URL's met behulp van blog mislukt omdat de waarden { controller = Home, action = Index } niet overeenkomen { controller = Blog, action = Article }. Routering valt vervolgens terug om te proberen default, wat slaagt.

Areas

Gebieden zijn een MVC-functie die wordt gebruikt voor het organiseren van gerelateerde functionaliteit in een groep als afzonderlijke:

  • Routeringsnaamruimte voor controlleracties.
  • Mapstructuur voor weergaven.

Als u gebieden gebruikt, kan een app meerdere controllers met dezelfde naam hebben, zolang ze verschillende gebieden hebben. Met behulp van gebieden wordt een hiërarchie gemaakt voor routering door een andere routeparameter area toe te voegen aan controller en action. In deze sectie wordt beschreven hoe routering communiceert met gebieden. Zie Gebieden voor meer informatie over hoe gebieden in combinatie met weergaven worden gebruikt.

In het volgende voorbeeld wordt MVC geconfigureerd voor het gebruik van de standaard conventionele route en een area route voor een area met de naam Blog.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{    
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");

app.Run();

In de voorgaande code MapAreaControllerRoute wordt aangeroepen om de "blog_route". De tweede parameter, "Blog"is de gebiedsnaam.

Wanneer het overeenkomt met een URL-pad zoals /Manage/Users/AddUser, genereert de "blog_route" route de routewaarden { area = Blog, controller = Users, action = AddUser }. De area routewaarde wordt geproduceerd door een standaardwaarde voor area. De route die is gemaakt door MapAreaControllerRoute , is gelijk aan het volgende:

app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
        defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");

MapAreaControllerRoute maakt een route met zowel een standaardwaarde als een beperking voor area het gebruik van de opgegeven gebiedsnaam, in dit geval Blog. De standaardwaarde zorgt ervoor dat de route altijd produceert { area = Blog, ... }. De beperking vereist de waarde { area = Blog, ... } voor het genereren van URL's.

Conventionele routering is orderafhankelijk. In het algemeen moeten routes met gebieden eerder worden geplaatst omdat ze specifieker zijn dan routes zonder een gebied.

In het voorgaande voorbeeld komen de routewaarden { area = Blog, controller = Users, action = AddUser } overeen met de volgende actie:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

Het kenmerk [Gebied] geeft een controller aan als onderdeel van een gebied. Deze controller bevindt zich in het Blog gebied. Controllers zonder kenmerk [Area] zijn geen lid van een gebied en komen niet overeen wanneer de area routewaarde wordt geleverd door routering. In het volgende voorbeeld kan alleen de eerste vermelde controller overeenkomen met de routewaarden { area = Blog, controller = Users, action = AddUser }.

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace2
{
    // Matches { area = Zebra, controller = Users, action = AddUser }
    [Area("Zebra")]
    public class UsersController : Controller
    {
        // GET /zebra/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace3
{
    // Matches { area = string.Empty, controller = Users, action = AddUser }
    // Matches { area = null, controller = Users, action = AddUser }
    // Matches { controller = Users, action = AddUser }
    public class UsersController : Controller
    {
        // GET /users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }
    }
}

De naamruimte van elke controller wordt hier weergegeven voor volledigheid. Als de voorgaande controllers dezelfde naamruimte gebruikten, wordt er een compilerfout gegenereerd. Klassenaamruimten hebben geen invloed op de routering van MVC.

De eerste twee controllers zijn leden van gebieden en komen alleen overeen wanneer hun respectieve gebiedsnaam wordt opgegeven door de area routewaarde. De derde controller is geen lid van een gebied en kan alleen overeenkomen wanneer er geen waarde area wordt opgegeven door routering.

In termen van de overeenkomende geen waarde betekent het ontbreken van de area waarde hetzelfde als wanneer de waarde voor area null of een lege tekenreeks is.

Bij het uitvoeren van een actie binnen een gebied wordt de routewaarde voor area als een omgevingswaarde beschikbaar, die moet worden gebruikt voor de routering en het genereren van URL's. Dit betekent dat standaard gebieden blijven hangen voor het genereren van URL's, zoals in het volgende voorbeeld wordt gedemonstreerd.

app.MapAreaControllerRoute(name: "duck_route",
                                     areaName: "Duck",
                                     pattern: "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute(name: "default",
                             pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace4
{
    [Area("Duck")]
    public class UsersController : Controller
    {
        // GET /Manage/users/GenerateURLInArea
        public IActionResult GenerateURLInArea()
        {
            // Uses the 'ambient' value of area.
            var url = Url.Action("Index", "Home");
            // Returns /Manage/Home/Index
            return Content(url);
        }

        // GET /Manage/users/GenerateURLOutsideOfArea
        public IActionResult GenerateURLOutsideOfArea()
        {
            // Uses the empty value for area.
            var url = Url.Action("Index", "Home", new { area = "" });
            // Returns /Manage
            return Content(url);
        }
    }
}

Met de volgende code wordt een URL gegenereerd voor /Zebra/Users/AddUser:

public class HomeController : Controller
{
    public IActionResult About()
    {
        var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
        return Content($"URL: {url}");
    }

Action definition

Openbare methoden op een controller, met uitzondering van methoden met het kenmerk NonAction , zijn acties.

Sample code

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

ASP.NET Core-controllers gebruiken de middleware routering om de URL's van binnenkomende aanvragen te vinden en deze toe te wijzen aan acties. Route templates:

  • Zijn gedefinieerd in opstartcode of kenmerken.
  • Beschrijven hoe URL-paden worden vergeleken met acties.
  • Worden gebruikt voor het genereren van URL's voor koppelingen. De gegenereerde koppelingen worden meestal geretourneerd in antwoorden.

Acties worden conventioneel gerouteerd of door kenmerken gerouteerd. Een route die op de controller of actie wordt geplaatst, is kenmerk-gerouteerd. Zie Gemengde routering voor meer informatie.

This document:

  • Hierin worden de interacties tussen MVC en routering uitgelegd:
    • Hoe typische MVC-apps gebruikmaken van routeringsfuncties.
    • Covers both:
    • Zie Routering voor geavanceerde routeringsdetails.
  • Verwijst naar het standaardrouteringssysteem dat is toegevoegd in ASP.NET Core 3.0, ook wel eindpuntroutering genoemd. Het is mogelijk om controllers te gebruiken met de vorige versie van routering voor compatibiliteitsdoeleinden. Zie de migratiehandleiding 2.2-3.0 voor instructies. Raadpleeg de versie 2.2 van dit document voor referentiemateriaal over het verouderde routeringssysteem.

Conventionele route instellen

Startup.Configure heeft doorgaans code die vergelijkbaar is met de volgende bij het gebruik van conventionele routering:

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

In het aanroepen van UseEndpoints wordt MapControllerRoute gebruikt om één enkele route te maken. De enkele route heeft de naam default route. De meeste apps met controllers en weergaven gebruiken een routesjabloon die vergelijkbaar is met de default route. REST API's moeten kenmerkroutering gebruiken.

De routesjabloon "{controller=Home}/{action=Index}/{id?}":

  • Komt overeen met een URL-pad, zoals /Products/Details/5

  • Extraheert de routewaarden { controller = Products, action = Details, id = 5 } door het pad te tokeniseren. De extractie van routewaarden resulteert in een overeenkomst als de app een controller heeft met de naam ProductsController en een Details actie:

    public class ProductsController : Controller
    {
        public IActionResult Details(int id)
        {
            return ControllerContext.MyDisplayRouteInfo(id);
        }
    }
    

    MyDisplayRouteInfo wordt geleverd door het Rick.Docs.Samples.RouteInfo NuGet-pakket en toont routegegevens.

  • /Products/Details/5 model koppelt de waarde van id = 5 aan de id parameter om deze in te stellen op 5. Zie Modelbinding voor meer informatie.

  • {controller=Home} definieert Home als de standaardwaarde controller.

  • {action=Index} definieert Index als de standaardwaarde action.

  • Het ? teken in {id?} definieert id als optioneel.

  • Standaard- en optionele routeparameters hoeven niet aanwezig te zijn in het URL-pad voor een overeenstemming. Zie De naslaginformatie over routesjablonen voor een gedetailleerde beschrijving van de syntaxis van de routesjabloon.

  • Komt overeen met het URL-pad /.

  • Produceert de routewaarden { controller = Home, action = Index }.

De waarden voor controller en action maken gebruik van de standaardwaarden. id produceert geen waarde omdat er geen corresponderend segment in het URL-pad is. / komt alleen overeen als er een HomeController en Index actie bestaat:

public class HomeController : Controller
{
  public IActionResult Index() { ... }
}

Met behulp van de voorgaande controllerdefinitie en routesjabloon wordt de HomeController.Index actie uitgevoerd voor de volgende URL-paden:

  • /Home/Index/17
  • /Home/Index
  • /Home
  • /

Het URL-pad / maakt gebruik van de routesjabloon met standaardcontrollers Home en de actie Index. Het URL-pad /Home maakt gebruik van de standaardactie Index voor de routesjabloon.

De gemaksmethode MapDefaultControllerRoute:

endpoints.MapDefaultControllerRoute();

Replaces:

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

Important

Routering wordt geconfigureerd met behulp van de UseRouting, MapControllerRouteen MapAreaControllerRoute middleware . Controllers gebruiken:

Conventional routing

Conventionele routering wordt gebruikt met controllers en weergaven. De default route:

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

Het voorgaande is een voorbeeld van een conventionele route. Dit wordt conventionele routering genoemd omdat er een conventie voor URL-paden wordt vastgesteld:

  • Het eerste padsegment, {controller=Home}wordt toegewezen aan de naam van de controller.
  • Het tweede segment, {action=Index}wordt toegewezen aan de naam van de actie.
  • Het derde segment {id?} wordt gebruikt voor een optioneel id. De ? in {id?} maakt het optioneel. id wordt gebruikt om toe te wijzen aan een modelentiteit.

Met deze default route wordt het URL-pad gebruikt:

  • /Products/List wordt toegewezen aan de ProductsController.List actie.
  • /Blog/Article/17 komt overeen met BlogController.Article en meestal wordt de id-parameter gekoppeld aan 17.

This mapping:

  • Gebaseerd op alleen de controller en actienamen.
  • Is niet gebaseerd op naamruimten, bronbestandslocaties of methodeparameters.

Met conventionele routering met de standaardroute kunt u de app maken zonder dat u voor elke actie een nieuw URL-patroon hoeft op te stellen. Voor een app met CRUD-stijlacties heeft u consistentie nodig voor de URL's over controllers heen:

  • Helpt de code te vereenvoudigen.
  • Maakt de gebruikersinterface voorspelbaarder.

Warning

De id in de voorgaande code wordt gedefinieerd als optioneel door de routesjabloon. Acties kunnen worden uitgevoerd zonder de optionele id die is opgegeven als onderdeel van de URL. Over het algemeen, wanneer id wordt weggelaten uit de URL:

  • id wordt door middel van modelbinding ingesteld op 0.
  • Er is geen entiteit gevonden in de overeenkomende id == 0database.

Kenmerkroutering biedt fijnmazige controle om de ID verplicht te stellen voor sommige acties, maar niet voor andere. De documentatie bevat standaard optionele parameters, zoals id wanneer ze waarschijnlijk in het juiste gebruik worden weergegeven.

De meeste apps moeten een eenvoudig en beschrijvend routeringsschema kiezen, zodat URL's leesbaar en zinvol zijn. De standaard conventionele route {controller=Home}/{action=Index}/{id?}:

  • Ondersteunt een eenvoudig en beschrijvend routeringsschema.
  • Is een nuttig startpunt voor apps op basis van de gebruikersinterface.
  • Is de enige routesjabloon die nodig is voor veel web-UI-apps. Voor grotere web-UI-apps is een andere route met het gebruik van Areas vaak alles wat nodig is.

MapControllerRoute en MapAreaRoute :

  • Wijs automatisch een orderwaarde toe aan hun eindpunten op basis van de volgorde die ze worden aangeroepen.

Eindpuntroutering in ASP.NET Core 3.0 of hoger:

  • Heeft geen concept van routes.
  • Biedt geen bestelgaranties voor de uitvoering van uitbreidbaarheid, alle eindpunten worden in één keer verwerkt.

Schakel logging in om te zien hoe de ingebouwde routeringimplementaties verzoeken afhandelen, zoals Route.

Kenmerkroutering wordt verderop in dit document uitgelegd.

Meerdere conventionele routes

Meerdere conventionele routes kunnen binnen UseEndpoints worden toegevoegd door meer aanroepen toe te voegen aan MapControllerRoute en MapAreaControllerRoute. Hiermee kunt u meerdere conventies definiëren of conventionele routes toevoegen die zijn toegewezen aan een specifieke actie, zoals:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

De blog route in de voorgaande code is een toegewezen conventionele route. Dit wordt een speciale conventionele route genoemd omdat:

Omdat controller en action niet worden weergegeven in de routesjabloon "blog/{*article}" als parameters:

  • Ze kunnen alleen de standaardwaarden { controller = "Blog", action = "Article" }hebben.
  • Deze route wordt altijd toegewezen aan de actie BlogController.Article.

/Blog, /Blog/Articleen /Blog/{any-string} zijn de enige URL-paden die overeenkomen met de blogroute.

Het voorgaande voorbeeld:

  • De blog route heeft een hogere prioriteit bij het koppelen dan de default route, omdat deze eerst wordt toegevoegd.
  • Is een voorbeeld van slug-stijlroutering , waarbij het gebruikelijk is om een artikelnaam te hebben als onderdeel van de URL.

Warning

In ASP.NET Core 3.0 of hoger werkt routering niet zoals:

  • Definieer een concept dat een route wordt genoemd. UseRouting voegt routekoppeling toe aan de middleware-pijplijn. De UseRouting middleware bekijkt de set eindpunten die in de app zijn gedefinieerd en selecteert de beste eindpuntovereenkomst op basis van de aanvraag.
  • Garanties bieden over de uitvoeringsvolgorde van uitbreidbaarheid, zoals IRouteConstraint of IActionConstraint.

Zie Routering voor referentiemateriaal over routering.

Conventionele routeringsvolgorde

Conventionele routering komt alleen overeen met een combinatie van actie en controller die door de app worden gedefinieerd. Dit is bedoeld om gevallen waarin conventionele routes elkaar overlappen te vereenvoudigen. Routes toevoegen met behulp van MapControllerRoute, MapDefaultControllerRoute en MapAreaControllerRoute wijst automatisch een orderwaarde toe aan hun eindpunten op basis van de volgorde waarin ze worden aangeroepen. Overeenkomsten van een route die eerder wordt weergegeven, hebben een hogere prioriteit. Conventionele routering is orderafhankelijk. In het algemeen moeten routes met gebieden eerder worden geplaatst omdat ze specifieker zijn dan routes zonder een gebied. Toegewezen conventionele routes met catch-all routeparameters, zoals {*article} kunnen een route te greedy maken, wat betekent dat deze overeenkomt met URL's die u hebt bedoeld om te worden vergeleken met andere routes. Plaats de gretige routes later in de routetabel om gretige overeenkomsten te voorkomen.

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.

Ambigu acties oplossen

Wanneer twee eindpunten overeenkomen via routering, moet routering een van de volgende handelingen uitvoeren:

  • Kies de beste kandidaat.
  • Gooi een uitzondering.

For example:

    public class Products33Controller : Controller
    {
        public IActionResult Edit(int id)
        {
            return ControllerContext.MyDisplayRouteInfo(id);
        }

        [HttpPost]
        public IActionResult Edit(int id, Product product)
        {
            return ControllerContext.MyDisplayRouteInfo(id, product.name);
        }
    }
}

De voorgaande controller definieert twee acties die overeenkomen met:

  • Het URL-pad /Products33/Edit/17
  • Routegegevens { controller = Products33, action = Edit, id = 17 }.

Dit is een typisch patroon voor MVC-controllers:

  • Edit(int) geeft een formulier weer om een product te bewerken.
  • Edit(int, Product) verwerkt het geplaatste formulier.

De juiste route oplossen:

  • Edit(int, Product) is geselecteerd wanneer de aanvraag een HTTP POSTis.
  • Edit(int) is geselecteerd wanneer het HTTP-werkwoord iets anders is. Edit(int) wordt over het algemeen via GET.

De HttpPostAttribute, [HttpPost]wordt verstrekt aan routering, zodat deze kan kiezen op basis van de HTTP-methode van de aanvraag. De HttpPostAttribute maakt Edit(int, Product) een betere match dan Edit(int).

Het is belangrijk om inzicht te hebben in de rol van kenmerken zoals HttpPostAttribute. Vergelijkbare kenmerken worden gedefinieerd voor andere HTTP-woorden. Bij conventionele routering is het gebruikelijk dat acties dezelfde actienaam gebruiken wanneer ze deel uitmaken van een toonformulier of het indienen van een formulierworkflow. Zie bijvoorbeeld de twee bewerkingsactiemethoden onderzoeken.

Als routering geen beste kandidaat kan kiezen, wordt er een AmbiguousMatchException gegenereerd, waarin de meerdere overeenkomende eindpunten worden vermeld.

Conventionele routenamen

De tekenreeksen "blog" en "default" in de volgende voorbeelden zijn conventionele routenamen:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

De routenamen geven de route een logische naam. De benoemde route kan worden gebruikt voor het genereren van URL's. Het gebruik van een benoemde route vereenvoudigt het maken van URL's wanneer de volgorde van routes ingewikkeld kan maken voor het genereren van URL's. Routenamen moeten uniek zijn voor de hele toepassing.

Route names:

  • Geen invloed hebben op URL-matchen of verzoekafhandeling.
  • Worden alleen gebruikt voor het genereren van URL's.

Het concept van de routenaam wordt weergegeven in routering als IEndpointNameMetadata. De termen routenaam en eindpuntnaam:

  • Are interchangeable.
  • Welke wordt gebruikt in documentatie en code, is afhankelijk van de API die wordt beschreven.

Kenmerkroutering voor REST API's

REST API's moeten kenmerkroutering gebruiken om de functionaliteit van de app te modelleren als een set resources waarin bewerkingen worden vertegenwoordigd door HTTP-woorden.

Kenmerkroutering maakt gebruik van een set kenmerken om acties rechtstreeks toe te wijzen aan routesjablonen. De volgende StartUp.Configure code is typisch voor een REST API en wordt gebruikt in het volgende voorbeeld:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

In de voorgaande code wordt MapControllers binnen UseEndpoints aangeroepen om controllers met eigenschapsrouting in kaart te brengen.

In het volgende voorbeeld:

  • HomeController komt overeen met een set URL's die vergelijkbaar zijn met de standaardconventionele route {controller=Home}/{action=Index}/{id?} .
public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

De HomeController.Index actie wordt uitgevoerd voor een van de URL-paden/, /Homeof /Home/Index/Home/Index/3.

In dit voorbeeld wordt een belangrijk programmeerverschil tussen kenmerkroutering en conventionele routering gemarkeerd. Voor kenmerkroutering is meer invoer vereist om een route op te geven. De conventionele standaardroute verwerkt routes beknopter. Met kenmerkroutering kunt u echter nauwkeurig bepalen welke routesjablonen van toepassing zijn op elke actie.

Bij kenmerkroutering spelen de controller- en actienamen geen rol waarin actie wordt vergeleken, tenzij tokenvervanging wordt gebruikt. Het volgende voorbeeld komt overeen met dezelfde URL's als in het vorige voorbeeld:

public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

De volgende code maakt gebruik van tokenvervanging voor action en controller:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("[controller]/[action]")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("[controller]/[action]")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

De volgende code is van toepassing op [Route("[controller]/[action]")] de controller:

[Route("[controller]/[action]")]
public class HomeController : Controller
{
    [Route("~/")]
    [Route("/Home")]
    [Route("~/Home/Index")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

In de voorgaande code moeten de Index methodesjablonen voorafgegaan worden door / of ~/ bij de routesjablonen. Routesjablonen die worden toegepast op een actie die begint met / of ~/ niet worden gecombineerd met routesjablonen die zijn toegepast op de controller.

Zie de prioriteit van routesjablonen voor informatie over de selectie van routesjablonen.

Gereserveerde routeringsnamen

De volgende trefwoorden zijn gereserveerde routeparameternamen bij het gebruik van Controllers of Razor Pages:

  • action
  • area
  • controller
  • handler
  • page

Het gebruik page als routeparameter met kenmerkroutering is een veelvoorkomende fout. Dit leidt tot inconsistent en verwarrend gedrag met het genereren van URL's.

public class MyDemo2Controller : Controller
{
    [Route("/articles/{page}")]
    public IActionResult ListArticles(int page)
    {
        return ControllerContext.MyDisplayRouteInfo(page);
    }
}

De speciale parameternamen worden door de URL-generatie gebruikt om te bepalen of een URL-generatiebewerking verwijst naar een Razor pagina of naar een controller.

De volgende trefwoorden zijn gereserveerd in de context van een Razor weergave of een Razor pagina:

  • page
  • using
  • namespace
  • inject
  • section
  • inherits
  • model
  • addTagHelper
  • removeTagHelper

Deze trefwoorden mogen niet worden gebruikt voor koppelingsgeneraties, modelgebonden parameters of eigenschappen op het hoogste niveau.

HTTP-werkwoordsjablonen

ASP.NET Core heeft de volgende HTTP-werkwoordsjablonen:

Route templates

ASP.NET Core heeft de volgende routesjablonen:

Kenmerkroutering met Http-werkwoordkenmerken

Houd rekening met de volgende controller:

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

In de voorgaande code:

  • Elke actie bevat het [HttpGet] kenmerk, dat alleen overeenkomt met HTTP GET-aanvragen.
  • De GetProduct actie bevat de "{id}" sjabloon en wordt daarom id toegevoegd aan de "api/[controller]" sjabloon op de controller. De sjabloon voor methoden is "api/[controller]/{id}". Daarom komt deze actie alleen overeen met GET-aanvragen voor het formulier/api/test2/xyz/api/test2/123,/api/test2/{any string} enzovoort.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • De GetIntProduct actie bevat de "int/{id:int}" sjabloon. Het :int gedeelte van de sjabloon beperkt de id routewaarden tot tekenreeksen die kunnen worden geconverteerd naar een geheel getal. Een GET-aanvraag voor /api/test2/int/abc:
    • Komt niet overeen met deze actie.
    • Retourneert een fout 404 Niet Gevonden.
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • De GetInt2Product actie bevat {id} in de sjabloon, maar legt geen beperkingen op aan id tot waarden die kunnen worden geconverteerd naar een geheel getal. Een GET-aanvraag voor /api/test2/int2/abc:
    • Komt overeen met deze route.
    • Modelbinding kan abc niet omzetten naar een geheel getal. De id parameter van de methode is geheel getal.
    • Retourneert een 400 Bad Request omdat het niet gelukt is om abc naar een geheel getal te converteren.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

Kenmerkroutering kan gebruikmaken van HttpMethodAttribute kenmerken zoals HttpPostAttribute, HttpPutAttributeen HttpDeleteAttribute. Alle HTTP-werkwoordkenmerken accepteren een routesjabloon. In het volgende voorbeeld ziet u twee acties die overeenkomen met dezelfde routesjabloon:

[ApiController]
public class MyProductsController : ControllerBase
{
    [HttpGet("/products3")]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpPost("/products3")]
    public IActionResult CreateProduct(MyProduct myProduct)
    {
        return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
    }
}

Het URL-pad gebruiken /products3:

  • De MyProductsController.ListProducts-actie wordt uitgevoerd wanneer het HTTP-werkwoordGET is.
  • De MyProductsController.CreateProduct-actie wordt uitgevoerd wanneer het HTTP-werkwoordPOST is.

Bij het bouwen van een REST API is het zeldzaam dat u moet gebruiken [Route(...)] voor een actiemethode, omdat de actie alle HTTP-methoden accepteert. Het is beter om het specifiekere HTTP-werkwoordkenmerk te gebruiken om precies te zijn wat uw API ondersteunt. Clients van REST API's zullen naar verwachting weten welke paden en HTTP-werkwoorden zijn toegewezen aan specifieke logische bewerkingen.

REST API's moeten kenmerkroutering gebruiken om de functionaliteit van de app te modelleren als een set resources waarin bewerkingen worden vertegenwoordigd door HTTP-woorden. Dit betekent dat veel bewerkingen, bijvoorbeeld GET en POST op dezelfde logische resource, dezelfde URL gebruiken. Kenmerkroutering biedt een beheerniveau dat nodig is om de indeling van een openbaar eindpunt van een API zorgvuldig te ontwerpen.

Omdat een kenmerkroute van toepassing is op een specifieke actie, is het eenvoudig om parameters vereist te maken als onderdeel van de definitie van de routesjabloon. In het volgende voorbeeld id is dit vereist als onderdeel van het URL-pad:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

De Products2ApiController.GetProduct(int) actie:

  • Wordt uitgevoerd met een url-pad zoals /products2/3
  • Wordt niet uitgevoerd met het URL-pad /products2.

Met het kenmerk [Verbruiken] kan een actie de ondersteunde inhoudstypen voor aanvragen beperken. Zie Ondersteunde aanvraaginhoudstypen definiëren met het kenmerk Verbruiken voor meer informatie.

Zie Routering voor een volledige beschrijving van routesjablonen en gerelateerde opties.

Zie [ApiController] voor meer informatie.

Route name

De volgende code definieert een routenaam van Products_List:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Routenamen kunnen worden gebruikt om een URL te genereren op basis van een specifieke route. Route names:

  • Dit heeft geen invloed op het URL-overeenkomende gedrag van routering.
  • Worden alleen gebruikt voor het genereren van URL's.

Routenamen moeten uniek zijn voor de hele toepassing.

Vergelijk de voorgaande code met de conventionele standaardroute, die de id parameter definieert als optioneel ({id?}). De mogelijkheid om API's nauwkeurig op te geven heeft voordelen, zoals het toestaan dat /products en /products/5 worden uitgevoerd door verschillende acties.

Het combineren van kenmerkroutes

Om kenmerkroutering minder herhalend te maken, worden routekenmerken op de controller gecombineerd met routekenmerken voor de afzonderlijke acties. Routesjablonen die op de controller zijn gedefinieerd, worden toegevoegd aan de routesjablonen van de acties. Als u een routekenmerk op de controller plaatst, worden alle acties in de controller gebruikt om kenmerkroutering te gebruiken.

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
    [HttpGet]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

In het voorgaande voorbeeld:

  • Het URL-pad /products kan overeenkomen met ProductsApi.ListProducts
  • Het URL-pad /products/5 kan overeenkomen met ProductsApi.GetProduct(int).

Beide acties komen alleen overeen met HTTP GET omdat ze zijn gemarkeerd met het [HttpGet] kenmerk.

Routesjablonen die worden toegepast op een actie die begint met / of ~/ niet worden gecombineerd met routesjablonen die zijn toegepast op de controller. Het volgende voorbeeld komt overeen met een set URL-paden die vergelijkbaar zijn met de standaardroute.

[Route("Home")]
public class HomeController : Controller
{
    [Route("")]
    [Route("Index")]
    [Route("/")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("About")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

In de volgende tabel worden de [Route] kenmerken in de voorgaande code uitgelegd:

Attribute Combineert met [Route("Home")] Definieert routesjabloon
[Route("")] Yes "Home"
[Route("Index")] Yes "Home/Index"
[Route("/")] No ""
[Route("About")] Yes "Home/About"

Routevolgorde kenmerk

Routering bouwt een structuur en komt overeen met alle eindpunten tegelijk:

  • De routevermeldingen gedragen zich alsof ze in een ideale volgorde worden geplaatst.
  • De meest specifieke routes hebben de kans om uit te voeren vóór de meer algemene routes.

Een kenmerkroute zoals blog/search/{topic} is bijvoorbeeld specifieker dan een kenmerkroute zoals blog/{*article}. De blog/search/{topic} route heeft standaard een hogere prioriteit, omdat deze specifieker is. Met conventionele routering is de ontwikkelaar verantwoordelijk voor het plaatsen van routes in de gewenste volgorde.

Kenmerkroutes kunnen een order configureren met behulp van de Order eigenschap. Alle door het framework geleverde routekenmerken omvatten Order . Routes worden verwerkt op basis van een oplopend type eigenschap Order . De standaardvolgorde is 0. Een route instellen met behulp van Order = -1 uitvoeringen voor routes die geen order instellen. Een route instellen met behulp van Order = 1 vindt plaats na de standaard routevolgorde.

Vermijd afhankelijk Ordervan . Als de URL-ruimte van een app expliciete volgordewaarden vereist om correct te routeren, is het waarschijnlijk ook verwarrend voor clients. Over het algemeen selecteert kenmerkroutering de juiste route met URL-overeenkomsten. Als de standaardvolgorde die wordt gebruikt voor het genereren van URL's niet werkt, is het gebruik van een routenaam als onderdrukking meestal eenvoudiger dan het toepassen van de Order eigenschap.

Houd rekening met de volgende twee controllers die beide de routekoppeling /homedefiniëren:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Als u aanvraagt /home met de voorgaande code, wordt er een uitzondering gegenereerd die vergelijkbaar is met de volgende:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

 WebMvcRouting.Controllers.HomeController.Index
 WebMvcRouting.Controllers.MyDemoController.MyIndex

Als u een van de routekenmerken toevoegt Order , wordt de dubbelzinnigheid opgelost:

[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
    return ControllerContext.MyDisplayRouteInfo();
}

Met de voorgaande code /home wordt het HomeController.Index eindpunt uitgevoerd. Om naar de MyDemoController.MyIndex, aanvraag /home/MyIndexte gaan . Note:

  • De voorgaande code is een voorbeeld of een slecht routeringsontwerp. Deze is gebruikt om de Order eigenschap te illustreren.
  • De Order eigenschap lost alleen de dubbelzinnigheid op, die sjabloon kan niet worden vergeleken. Het is beter om de [Route("Home")] sjabloon te verwijderen.

Zie Razor route- en app-conventies voor pagina's: Routevolgorde voor informatie over routevolgordes met Razor Pagina's.

In sommige gevallen wordt een HTTP 500-fout geretourneerd met ambigu routes. Gebruik loggen om te zien welke eindpunten de AmbiguousMatchExceptionveroorzaken.

Tokenvervanging in routesjablonen [controller], [actie], [gebied]

Voor het gemak ondersteunen kenmerkroutes tokenvervanging door een token tussen vierkante haken ([, ]) te sluiten. De tokens [action], [area]en [controller] worden vervangen door de waarden van de actienaam, gebiedsnaam en controllernaam uit de actie waarin de route is gedefinieerd:

[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
    [HttpGet]
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }


    [HttpGet("{id}")]
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

In de voorgaande code:

[HttpGet]
public IActionResult List()
{
    return ControllerContext.MyDisplayRouteInfo();
}
  • Wedstrijden /Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
    return ControllerContext.MyDisplayRouteInfo(id);
}
  • Wedstrijden /Products0/Edit/{id}

Tokenvervanging vindt plaats als de laatste stap van het bouwen van de attribuutroutes. Het voorgaande voorbeeld gedraagt zich hetzelfde als de volgende code:

public class Products20Controller : Controller
{
    [HttpGet("[controller]/[action]")]  // Matches '/Products20/List'
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("[controller]/[action]/{id}")]   // Matches '/Products20/Edit/{id}'
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Als u dit leest in een andere taal dan Engels, laat het ons dan weten in dit GitHub-discussieprobleem als u de codeopmerkingen in uw eigen taal wilt zien.

Attribuutsroutes kunnen ook worden gecombineerd met overerving. De kracht hiervan komt goed tot uiting wanneer het wordt gecombineerd met tokenvervanging. Tokenvervanging is ook van toepassing op routenamen die zijn gedefinieerd door kenmerkroutes. [Route("[controller]/[action]", Name="[controller]_[action]")]genereert een unieke routenaam voor elke actie:

[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}

public class Products11Controller : MyBase2Controller
{
    [HttpGet]                      // /api/products11/list
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]             //    /api/products11/edit/3
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Als u het letterlijke scheidingsteken voor tokenvervanging [ wilt vergelijken of ], escapet u het door het teken ([[ of ]]) te herhalen.

Een parametertransformatie gebruiken om tokenvervanging aan te passen

Tokenvervanging kan worden aangepast met behulp van een parametertransformator. Een parametertransformator implementeert IOutboundParameterTransformer en transformeert de waarde van parameters. Een aangepaste SlugifyParameterTransformer parametertransformator wijzigt bijvoorbeeld de SubscriptionManagement routewaarde in subscription-management:

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

Dit RouteTokenTransformerConvention is een toepassingsmodelconventie die:

  • Hiermee past u een parametertransformatie toe op alle kenmerkroutes in een toepassing.
  • Hiermee worden de waarden van het kenmerkroutetoken aangepast wanneer ze worden vervangen.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

De voorgaande ListAll methode komt overeen /subscription-management/list-all.

De RouteTokenTransformerConvention is geregistreerd als een optie in ConfigureServices.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        options.Conventions.Add(new RouteTokenTransformerConvention(
                                     new SlugifyParameterTransformer()));
    });
}

Zie MDN-webdocumenten op Slug voor de definitie van Slug.

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 kenmerkroutes

Kenmerkroutering ondersteunt het definiëren van meerdere routes die dezelfde actie bereiken. Het meest voorkomende gebruik hiervan is om het gedrag van de standaard conventionele route na te bootsen, zoals wordt weergegeven in het volgende voorbeeld:

[Route("[controller]")]
public class Products13Controller : Controller
{
    [Route("")]     // Matches 'Products13'
    [Route("Index")] // Matches 'Products13/Index'
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

Als u meerdere routekenmerken op de controller plaatst, betekent dit dat elke eigenschap wordt gecombineerd met elk van de routekenmerken op de actiemethoden:

[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
    [HttpPost("Buy")]       // Matches 'Products6/Buy' and 'Store/Buy'
    [HttpPost("Checkout")]  // Matches 'Products6/Checkout' and 'Store/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Alle http-bewerkingsroutebeperkingen worden geïmplementeerd IActionConstraint.

Wanneer meerdere routekenmerken die worden geïmplementeerd IActionConstraint op een actie worden geplaatst:

  • Elke actiebeperking combineert met de routesjabloon die is toegepast op de controller.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
    [HttpPut("Buy")]        // Matches PUT 'api/Products7/Buy'
    [HttpPost("Checkout")]  // Matches POST 'api/Products7/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Het gebruik van meerdere routes voor acties kan nuttig en krachtig lijken, het is beter om de URL-ruimte van uw app eenvoudig en goed gedefinieerd te houden. Gebruik meerdere routes voor acties alleen wanneer dat nodig is, bijvoorbeeld om bestaande clients te ondersteunen.

Optionele parameters, standaardwaarden en beperkingen voor kenmerkroute opgeven

Kenmerkroutes ondersteunen dezelfde inlinesyntaxis als conventionele routes om optionele parameters, standaardwaarden en beperkingen op te geven.

public class Products14Controller : Controller
{
    [HttpPost("product14/{id:int}")]
    public IActionResult ShowProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

In de voorgaande code [HttpPost("product14/{id:int}")] past u een routebeperking toe. De Products14Controller.ShowProduct-actie komt alleen overeen met URL-paden zoals /product14/3. Het gedeelte {id:int} van de routesjabloon beperkt dat segment tot alleen gehele getallen.

Zie De naslaginformatie over routesjablonen voor een gedetailleerde beschrijving van de syntaxis van de routesjabloon.

Aangepaste routekenmerken met IRouteTemplateProvider

Alle routekenmerken worden geïmplementeerd IRouteTemplateProvider. De ASP.NET Core-runtime:

  • Zoekt naar kenmerken in controllerklassen en actiemethoden wanneer de app wordt gestart.
  • Maakt gebruik van de kenmerken die worden geïmplementeerd IRouteTemplateProvider om de eerste set routes te bouwen.

Implementeren IRouteTemplateProvider om aangepaste routekenmerken te definiëren. Met elke IRouteTemplateProvider route kunt u één route definiëren met een aangepaste routesjabloon, volgorde en naam:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
    public string Template => "api/[controller]";
    public int? Order => 2;
    public string Name { get; set; }
}

[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
    // GET /api/MyTestApi
    [HttpGet]
    public IActionResult Get()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

De voorgaande Get methode retourneert Order = 2, Template = api/MyTestApi.

Toepassingsmodel gebruiken om kenmerkroutes aan te passen

Het toepassingsmodel:

  • Is een objectmodel dat bij het opstarten is gemaakt.
  • Bevat alle metagegevens die door ASP.NET Core worden gebruikt om de acties in een app te routeren en uit te voeren.

Het toepassingsmodel bevat alle gegevens die zijn verzameld uit routekenmerken. De gegevens van routekenmerken worden geleverd door de IRouteTemplateProvider implementatie. Conventions:

  • Kan worden geschreven om het toepassingsmodel te wijzigen om aan te passen hoe routering zich gedraagt.
  • Worden gelezen bij het opstarten van de app.

In deze sectie ziet u een eenvoudig voorbeeld van het aanpassen van routering met behulp van een toepassingsmodel. Met de volgende code worden routes grofweg afgestemd op de mapstructuur van het project.

public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
    private readonly string _baseNamespace;

    public NamespaceRoutingConvention(string baseNamespace)
    {
        _baseNamespace = baseNamespace;
    }

    public void Apply(ControllerModel controller)
    {
        var hasRouteAttributes = controller.Selectors.Any(selector =>
                                                selector.AttributeRouteModel != null);
        if (hasRouteAttributes)
        {
            return;
        }

        var namespc = controller.ControllerType.Namespace;
        if (namespc == null)
            return;
        var template = new StringBuilder();
        template.Append(namespc, _baseNamespace.Length + 1,
                        namespc.Length - _baseNamespace.Length - 1);
        template.Replace('.', '/');
        template.Append("/[controller]/[action]/{id?}");

        foreach (var selector in controller.Selectors)
        {
            selector.AttributeRouteModel = new AttributeRouteModel()
            {
                Template = template.ToString()
            };
        }
    }
}

Met de volgende code voorkomt u dat de namespace conventie wordt toegepast op controllers die door een kenmerk worden gerouteerd:

public void Apply(ControllerModel controller)
{
    var hasRouteAttributes = controller.Selectors.Any(selector =>
                                            selector.AttributeRouteModel != null);
    if (hasRouteAttributes)
    {
        return;
    }

De volgende controller gebruikt NamespaceRoutingConventionbijvoorbeeld niet:

[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
    // /managers/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        return Content($"Index- template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

De methode NamespaceRoutingConvention.Apply:

  • Doet niets als de controller wordt gerouteerd.
  • Hiermee stelt u de sjabloon controllers in op basis van de namespacesjabloon , waarbij de basis namespace is verwijderd.

De NamespaceRoutingConvention kan worden toegepast in Startup.ConfigureServices:

namespace My.Application
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews(options =>
            {
                options.Conventions.Add(
                    new NamespaceRoutingConvention(typeof(Startup).Namespace));
            });
        }
        // Remaining code ommitted for brevity.

Denk bijvoorbeeld aan de volgende controller:

using Microsoft.AspNetCore.Mvc;

namespace My.Application.Admin.Controllers
{
    public class UsersController : Controller
    {
        // GET /admin/controllers/users/index
        public IActionResult Index()
        {
            var fullname = typeof(UsersController).FullName;
            var template = 
                ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
            var path = Request.Path.Value;

            return Content($"Path: {path} fullname: {fullname}  template:{template}");
        }

        public IActionResult List(int? id)
        {
            var path = Request.Path.Value;
            return Content($"Path: {path} ID:{id}");
        }
    }
}

In de voorgaande code:

  • De basis namespace is My.Application.
  • De volledige naam van de voorgaande controller is My.Application.Admin.Controllers.UsersController.
  • Hiermee NamespaceRoutingConvention stelt u de controllersjabloon in op Admin/Controllers/Users/[action]/{id?.

De NamespaceRoutingConvention kan ook worden toegepast als een kenmerk op een controller:

[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
    // /admin/controllers/test/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        var actionname = ControllerContext.ActionDescriptor.ActionName;
        return Content($"Action- {actionname} template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

Gemengde routering: Kenmerkroutering versus conventionele routering

ASP.NET Core-apps kunnen het gebruik van conventionele routering en kenmerkroutering combineren. Het is gebruikelijk om conventionele routes te gebruiken voor controllers die HTML-pagina's voor browsers bedienen en kenmerkroutering voor controllers die API's bedienen REST .

Acties worden of conventioneel gerouteerd of via attributen gerouteerd. Wanneer je een route op de controller of de actie plaatst, wordt deze met attributen gerouteerd. Acties die kenmerkroutes definiëren, kunnen niet worden bereikt via de conventionele routes en omgekeerd. Elk routeattribuut op de controller maakt alle acties in de controller gerouteerd.

Kenmerkroutering en conventionele routering gebruiken dezelfde routeringsengine.

URL-generatie en omgevingswaarden

Apps kunnen functies voor het genereren van routerings-URL's gebruiken om URL-koppelingen naar acties te genereren. Het genereren van URL's elimineert hardcoderings-URL's, waardoor code robuuster en onderhoudbaarer wordt. Deze sectie is gericht op de functies voor het genereren van URL's die door MVC worden geboden en beslaan alleen de basisbeginselen van het genereren van URL's. Zie Routering voor een gedetailleerde beschrijving van het genereren van URL's.

De IUrlHelper interface is het onderliggende element van de infrastructuur tussen MVC en routering voor het genereren van URL's. Een exemplaar van IUrlHelper is beschikbaar via de Url eigenschap in controllers, weergaven en weergaveonderdelen.

In het volgende voorbeeld wordt de IUrlHelper interface gebruikt via de Controller.Url eigenschap om een URL naar een andere actie te genereren.

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Als de app gebruikmaakt van de standaard conventionele route, is de waarde van de url variabele de URL-padtekenreeks /UrlGeneration/Destination. Dit URL-pad wordt gemaakt door routering door het volgende te combineren:

  • De routewaarden van de huidige aanvraag, die omgevingswaarden worden genoemd.
  • De waarden die worden doorgegeven aan Url.Action en die waarden invoegen in de routesjabloon:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

Elke routeparameter in de routesjabloon wordt vervangen door namen die overeenkomen met de waarden en omgevingswaarden. Een routeparameter die geen waarde heeft, kan:

  • Gebruik een standaardwaarde als deze een waarde heeft.
  • Overgeslagen als dit optioneel is. Bijvoorbeeld de id van de routesjabloon {controller}/{action}/{id?}.

Het genereren van url's mislukt als een vereiste routeparameter geen bijbehorende waarde heeft. Als het genereren van url's voor een route mislukt, wordt de volgende route geprobeerd totdat alle routes zijn geprobeerd of een overeenkomst wordt gevonden.

In het voorgaande voorbeeld wordt uitgegaan van Url.Actionconventionele routering. Het genereren van URL's werkt op dezelfde manier met kenmerkroutering, hoewel de concepten verschillen. Bij gebruik van conventionele routering:

  • De routewaarden worden gebruikt om een sjabloon uit te vouwen.
  • De routewaarden voor controller en action worden meestal weergegeven in die sjabloon. Dit werkt omdat de URL's die overeenkomen met routering voldoen aan een conventie.

In het volgende voorbeeld wordt kenmerkroutering gebruikt:

public class UrlGenerationAttrController : Controller
{
    [HttpGet("custom")]
    public IActionResult Source()
    {
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination")]
    public IActionResult Destination()
    {
       return ControllerContext.MyDisplayRouteInfo();
    }
}

De Source actie in de voorgaande code genereert custom/url/to/destination.

LinkGenerator is toegevoegd in ASP.NET Core 3.0 als alternatief voor IUrlHelper. LinkGenerator biedt vergelijkbare maar flexibelere functionaliteit. Elke methode op IUrlHelper heeft ook een bijbehorende reeks methoden LinkGenerator .

URL's genereren op actienaam

Url.Action, LinkGenerator.GetPathByAction en alle gerelateerde overbelastingen zijn ontworpen om het doeleindpunt te genereren door een controllernaam en actienaam op te geven.

Wanneer u Url.Action gebruikt, worden de huidige routewaarden voor controller en action geleverd door de runtime:

  • De waarde van controller en action maken deel uit van zowel omgevingswaarden als waarden. De methode Url.Action gebruikt altijd de huidige waarden van action en controller genereert een URL-pad dat naar de huidige actie routeert.

Routering probeert de waarden in omgevingswaarden te gebruiken om informatie in te vullen die niet is opgegeven bij het genereren van een URL. Overweeg een route zoals {a}/{b}/{c}/{d} met omgevingswaarden { a = Alice, b = Bob, c = Carol, d = David }:

  • Routering bevat voldoende informatie om een URL te genereren zonder extra waarden.
  • Routering heeft voldoende informatie omdat alle routeparameters een waarde hebben.

Als de waarde { d = Donovan } wordt toegevoegd:

  • De waarde { d = David } wordt genegeerd.
  • Het gegenereerde URL-pad is Alice/Bob/Carol/Donovan.

Waarschuwing: URL-paden zijn hiërarchisch. Als in het voorgaande voorbeeld de waarde { c = Cheryl } wordt toegevoegd:

  • Beide waarden { c = Carol, d = David } worden genegeerd.
  • Er is geen waarde meer voor d en het genereren van URL's mislukt.
  • De gewenste waarden van c en d moeten worden opgegeven om een URL te genereren.

U kunt verwachten dat dit probleem zich voordoet met de standaardroute {controller}/{action}/{id?}. Dit probleem komt zelden voor in de praktijk omdat Url.Action altijd expliciet een controller- en action-waarde specificeert.

Verschillende overbelastingen van URL.Action voeren een routewaardenobject uit om waarden op te geven voor andere routeparameters dan controller en action. Het object voor routewaarden wordt vaak gebruikt met id. Bijvoorbeeld: Url.Action("Buy", "Products", new { id = 17 }). Het routewaardenobject:

  • Volgens conventie is een object meestal van anoniem type.
  • Kan een IDictionary<> of een POCO zijn).

Eventuele extra routewaarden die niet overeenkomen met routeparameters, worden in de querytekenreeks geplaatst.

public IActionResult Index()
{
    var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
    return Content(url);
}

De voorgaande code genereert /Products/Buy/17?color=red.

Met de volgende code wordt een absolute URL gegenereerd:

public IActionResult Index2()
{
    var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
    // Returns https://localhost:5001/Products/Buy/17
    return Content(url);
}

Gebruik een van de volgende manieren om een absolute URL te maken:

  • Een overload-functie die een protocol accepteert. Bijvoorbeeld de voorgaande code.
  • LinkGenerator.GetUriByAction, waarmee standaard absolute URI's worden gegenereerd.

URLs per route genereren

In de voorgaande code is het genereren van een URL gedemonstreerd door de controller en de actienaam door te geven. IUrlHelper biedt ook de url.RouteUrl-reeks methoden. Deze methoden zijn vergelijkbaar met Url.Action, maar ze kopiëren niet de huidige waarden van action en controller naar de routewaarden. Het meest voorkomende gebruik van Url.RouteUrl:

  • Hiermee geeft u een routenaam op voor het genereren van de URL.
  • Over het algemeen specificeer je geen controller- of actienaam.
public class UrlGeneration2Controller : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.RouteUrl("Destination_Route");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

In het volgende Razor bestand wordt een HTML-koppeling naar het Destination_Routevolgende bestand gegenereerd:

<h1>Test Links</h1>

<ul>
    <li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>

URLs genereren in HTML en Razor

IHtmlHelperbiedt de HtmlHelper methoden Html.BeginForm en Html.ActionLink om respectievelijk elementen te genereren<form>.<a> Deze methoden gebruiken de methode Url.Action om een URL te genereren en ze accepteren vergelijkbare argumenten. De metgezellen voor Url.RouteUrl zijn HtmlHelper en Html.BeginRouteForm, die vergelijkbare functionaliteit hebben.

TagHelpers genereren URL's met behulp van de TagHelper form en de TagHelper <a>. Beide gebruiken IUrlHelper voor hun implementatie. Zie Tag Helpers in formulieren voor meer informatie.

In weergaven is de IUrlHelper functie beschikbaar via de Url eigenschap voor elke ad-hoc-URL-generatie die niet wordt gedekt door het bovenstaande.

URL-generatie in actieresultaten

In de voorgaande voorbeelden is het gebruik IUrlHelper in een controller getoond. Het meest voorkomende gebruik in een controller is het genereren van een URL als onderdeel van een actieresultaat.

De ControllerBase en Controller basisklassen bieden handige methoden voor actieresultaten die verwijzen naar een andere actie. Een typisch gebruik is om om te leiden na het accepteren van gebruikersinvoer:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
    if (ModelState.IsValid)
    {
        // Update DB with new details.
        ViewData["Message"] = $"Successful edit of customer {id}";
        return RedirectToAction("Index");
    }
    return View(customer);
}

De actie resulteert in factory-methoden zoals RedirectToAction en CreatedAtAction volgen een vergelijkbaar patroon als de methoden op IUrlHelper.

Speciaal geval voor speciale conventionele routes

Conventionele routering kan een speciaal soort routedefinitie gebruiken, een speciale conventionele route genoemd. In het volgende voorbeeld is de benoemde blog route een toegewezen conventionele route:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

Met behulp van de voorgaande routedefinities Url.Action("Index", "Home") genereert u het URL-pad / met behulp van de default route, maar waarom? U kunt raden dat de routewaarden { controller = Home, action = Index } voldoende zijn om een URL te genereren met behulp van blog, en het resultaat zou zijn /blog?action=Index&controller=Home.

Toegewezen conventionele routes zijn afhankelijk van een speciaal gedrag van standaardwaarden die geen bijbehorende routeparameter hebben die voorkomt dat de route te greedy is met het genereren van URL's. In dit geval zijn de standaardwaarden en { controller = Blog, action = Article }worden ze controller niet action weergegeven als routeparameter. Wanneer routering URL-generatie uitvoert, moeten de opgegeven waarden overeenkomen met de standaardwaarden. Het genereren van URL's met behulp van blog mislukt omdat de waarden { controller = Home, action = Index } niet overeenkomen { controller = Blog, action = Article }. Routering valt vervolgens terug om te proberen default, wat slaagt.

Areas

Gebieden zijn een MVC-functie die wordt gebruikt voor het organiseren van gerelateerde functionaliteit in een groep als afzonderlijke:

  • Routeringsnaamruimte voor controlleracties.
  • Mapstructuur voor weergaven.

Als u gebieden gebruikt, kan een app meerdere controllers met dezelfde naam hebben, zolang ze verschillende gebieden hebben. Met behulp van gebieden wordt een hiërarchie gemaakt voor routering door een andere routeparameter area toe te voegen aan controller en action. In deze sectie wordt beschreven hoe routering communiceert met gebieden. Zie Gebieden voor meer informatie over hoe gebieden in combinatie met weergaven worden gebruikt.

In het volgende voorbeeld wordt MVC geconfigureerd voor het gebruik van de standaard conventionele route en een area route voor een area met de naam Blog.

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

In de voorgaande code MapAreaControllerRoute wordt aangeroepen om de "blog_route". De tweede parameter, "Blog"is de gebiedsnaam.

Wanneer het overeenkomt met een URL-pad zoals /Manage/Users/AddUser, genereert de "blog_route" route de routewaarden { area = Blog, controller = Users, action = AddUser }. De area routewaarde wordt geproduceerd door een standaardwaarde voor area. De route die is gemaakt door MapAreaControllerRoute , is gelijk aan het volgende:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
        defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

MapAreaControllerRoute maakt een route met zowel een standaardwaarde als een beperking voor area het gebruik van de opgegeven gebiedsnaam, in dit geval Blog. De standaardwaarde zorgt ervoor dat de route altijd produceert { area = Blog, ... }. De beperking vereist de waarde { area = Blog, ... } voor het genereren van URL's.

Conventionele routering is orderafhankelijk. In het algemeen moeten routes met gebieden eerder worden geplaatst omdat ze specifieker zijn dan routes zonder een gebied.

In het voorgaande voorbeeld komen de routewaarden { area = Blog, controller = Users, action = AddUser } overeen met de volgende actie:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

Het kenmerk [Gebied] geeft een controller aan als onderdeel van een gebied. Deze controller bevindt zich in het Blog gebied. Controllers zonder kenmerk [Area] zijn geen lid van een gebied en komen niet overeen wanneer de area routewaarde wordt geleverd door routering. In het volgende voorbeeld kan alleen de eerste vermelde controller overeenkomen met de routewaarden { area = Blog, controller = Users, action = AddUser }.

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace2
{
    // Matches { area = Zebra, controller = Users, action = AddUser }
    [Area("Zebra")]
    public class UsersController : Controller
    {
        // GET /zebra/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace3
{
    // Matches { area = string.Empty, controller = Users, action = AddUser }
    // Matches { area = null, controller = Users, action = AddUser }
    // Matches { controller = Users, action = AddUser }
    public class UsersController : Controller
    {
        // GET /users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }
    }
}

De naamruimte van elke controller wordt hier weergegeven voor volledigheid. Als de voorgaande controllers dezelfde naamruimte gebruikten, wordt er een compilerfout gegenereerd. Klassenaamruimten hebben geen invloed op de routering van MVC.

De eerste twee controllers zijn leden van gebieden en komen alleen overeen wanneer hun respectieve gebiedsnaam wordt opgegeven door de area routewaarde. De derde controller is geen lid van een gebied en kan alleen overeenkomen wanneer er geen waarde area wordt opgegeven door routering.

In termen van de overeenkomende geen waarde betekent het ontbreken van de area waarde hetzelfde als wanneer de waarde voor area null of een lege tekenreeks is.

Bij het uitvoeren van een actie binnen een gebied wordt de routewaarde voor area als een omgevingswaarde beschikbaar, die moet worden gebruikt voor de routering en het genereren van URL's. Dit betekent dat standaard gebieden blijven hangen voor het genereren van URL's, zoals in het volgende voorbeeld wordt gedemonstreerd.

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute(name: "duck_route", 
                                     areaName: "Duck",
                                     pattern: "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute(name: "default",
                                 pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
});
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace4
{
    [Area("Duck")]
    public class UsersController : Controller
    {
        // GET /Manage/users/GenerateURLInArea
        public IActionResult GenerateURLInArea()
        {
            // Uses the 'ambient' value of area.
            var url = Url.Action("Index", "Home");
            // Returns /Manage/Home/Index
            return Content(url);
        }

        // GET /Manage/users/GenerateURLOutsideOfArea
        public IActionResult GenerateURLOutsideOfArea()
        {
            // Uses the empty value for area.
            var url = Url.Action("Index", "Home", new { area = "" });
            // Returns /Manage
            return Content(url);
        }
    }
}

Met de volgende code wordt een URL gegenereerd voor /Zebra/Users/AddUser:

public class HomeController : Controller
{
    public IActionResult About()
    {
        var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
        return Content($"URL: {url}");
    }

Action definition

Openbare methoden op een controller, met uitzondering van methoden met het kenmerk NonAction , zijn acties.

Sample code

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