Routing zu Controlleraktionen in ASP.NET Core

Von Ryan Nowak, Kirk Larkin und Rick Anderson

ASP.NET Core Controller verwenden die Routing-Middleware, um den URLs eingehender Anforderungen zu entsprechen und sie Aktionen zuzuordnen. Routenvorlagen:

  • Werden beim Start in Program.cs oder in Attributen definiert.
  • Beschreiben Sie, wie URL-Pfade mit Aktionen übereinstimmen.
  • Werden verwendet, um URLs für Links zu generieren. Die generierten Links werden in der Regel in Antworten zurückgegeben.

Aktionen sind entweder konventionell weitergeleitet oder Attributrouten. Wenn Sie eine Route auf dem Controller oder einer Aktion platzieren, wird sie attributgeroutet. Weitere Informationen finden Sie im Abschnitt Gemischtes Routing.

Dieses Dokument hat folgende Eigenschaften:

  • Erläutert die Interaktionen zwischen MVC und Routing:
    • Wie typische MVC-Apps die Verwendung von Routingfeatures verwenden.
    • Deckt beides ab:
    • Weitere Informationen finden Sie unter Routing für erweiterte Routingdetails .
  • Bezieht sich auf das Standardroutingsystem namens Endpunktrouting. Es ist möglich, Controller mit der vorherigen Version des Routings für Kompatibilitätszwecke zu verwenden. Weitere Anweisungen finden Sie im Migrationshandbuch 2.2-3.0 .

Einrichten einer herkömmlichen Route

Die ASP.NET Core MVC-Vorlage generiert herkömmlichen Routingcode ähnlich wie folgt:

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 wird verwendet, um eine einzelne Route zu erstellen. Die einzelne Route ist die benannte default Route. Die meisten Apps mit Controllern und Ansichten verwenden eine Routenvorlage ähnlich der default Route. REST-APIs sollten Attributrouting verwenden.

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

Die Routevorlage "{controller=Home}/{action=Index}/{id?}":

  • Entspricht einem URL-Pfad wie /Products/Details/5

  • Extrahiert die Routenwerte { controller = Products, action = Details, id = 5 } , indem sie den Pfad tokenisieren. Die Extraktion von Routenwerten führt zu einer Übereinstimmung, wenn die App über einen Controller namens ProductsController und eine Details Aktion verfügt:

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

    MyDisplayRouteInfo wird von dem NuGet-Paket Rick.Docs.Samples.RouteInfo bereitgestellt und zeigt Routeninformationen an.

  • /Products/Details/5 Das Modell bindet den Wert id = 5 , um den id Parameter auf 5festzulegen. Weitere Details finden Sie unter Modellbindung .

  • {controller=Home}Home definiert als Standardcontroller.

  • {action=Index}Index definiert als Standardaction.

  • Das ? Zeichen in {id?} definiert id als optional.

    • Standardmäßige und optionale Routenparameter müssen nicht im URL-Pfad vorhanden sein, damit es eine Übereinstimmung gibt. Eine ausführliche Beschreibung der Syntax der Routenvorlage finden Sie unter Routenvorlagenreferenz.
  • Entspricht dem URL-Pfad /.

  • Erstellt die Routenwerte { controller = Home, action = Index }.

Die Werte für controller die action Verwendung der Standardwerte. id erzeugt keinen Wert, da kein entsprechendes Segment im URL-Pfad vorhanden ist. / nur übereinstimmen, wenn eine HomeControllerIndex Aktion vorhanden ist:

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

Mithilfe der vorhergehenden Controllerdefinitions- und Routenvorlage wird die HomeController.Index Aktion für die folgenden URL-Pfade ausgeführt:

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

Der URL-Pfad / verwendet die Standardcontroller Home und Index Aktion der Route-Vorlage. Der URL-Pfad /Home verwendet die Standardaktion Index für die Routevorlage.

Mit der Hilfsmethode MapDefaultControllerRoute:

app.MapDefaultControllerRoute();

Ersetzt:

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

Wichtig

Routing wird mithilfe der Middleware und der UseRoutingUseEndpoints Middleware konfiguriert. So verwenden Sie Controller:

Apps müssen UseRouting oder UseEndpoints normalerweise nicht aufrufen. WebApplicationBuilder konfiguriert eine Middlewarepipeline, die die in Program.cs hinzugefügte Middleware mit UseRouting und UseEndpoints umschließt. Weitere Informationen finden Sie unter Routing in ASP.NET Core.

Herkömmliches Routing

Herkömmliches Routing wird mit Controllern und Ansichten verwendet. Die default-Route:

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

Der vorhergehende ist ein Beispiel für eine herkömmliche Route. Es wird als herkömmliches Routing bezeichnet, da es eine Konvention für URL-Pfade erstellt:

  • Das erste Pfadsegment, , {controller=Home}entspricht dem Controllernamen.
  • Das zweite Segment, , {action=Index}entspricht dem Aktionsnamen .
  • Das dritte Segment {id?} wird für eine optionale id. Das ? In {id?} macht es optional. id wird verwendet, um einer Modellentität zuzuordnen.

Verwenden Sie diese default Route, den URL-Pfad:

  • /Products/List ordnet die ProductsController.List Aktion zu.
  • /Blog/Article/17BlogController.Article bezieht den Parameter in der id Regel auf 17.

Diese Zuordnung:

  • Basiert nur auf den Controller- und Aktionsnamen.
  • Basiert nicht auf Namespaces, Quelldateispeicherorten oder Methodenparametern.

Die Verwendung herkömmlicher Routing mit der Standardroute ermöglicht das Erstellen der App, ohne für jede Aktion ein neues URL-Muster zu erstellen. Für eine App mit CRUD-Formataktionen mit Konsistenz für die URLs für Controller:

  • Hilft beim Vereinfachen des Codes.
  • Macht die Benutzeroberfläche besser vorhersagbar.

Warnung

Der id vorherige Code wird als optional durch die Routevorlage definiert. Aktionen können ohne die optionale ID ausgeführt werden, die als Teil der URL bereitgestellt wird. Im Allgemeinen wirdid die URL ausgelassen:

  • id ist auf 0 die Modellbindung festgelegt.
  • Es wird keine Entität im Datenbankabgleich id == 0gefunden.

Das Attributrouting bietet ein fein zugeschnittenes Steuerelement, um die ID für einige Aktionen und nicht für andere personen erforderlich zu machen. In der Konvention enthält die Dokumentation optionale Parameter wie die id Wahrscheinlichkeit, dass sie in der richtigen Verwendung angezeigt werden.

Für die meisten Apps sollte eine grundlegendes und beschreibendes Routingschema ausgewählt werden, um lesbare und aussagekräftige URLs zu erhalten. Für die konventionelle Standardroute {controller=Home}/{action=Index}/{id?} gilt:

  • Sie unterstützt ein grundlegendes und beschreibendes Routingschema.
  • Sie stellt einen nützlichen Startpunkt für benutzeroberflächenbasierte Apps dar.
  • Ist die einzige Routenvorlage, die für viele Web-UI-Apps benötigt wird. Für größere Web-UI-Apps ist häufig eine andere Route mit Bereichen erforderlich.

MapControllerRoute und MapAreaRoute :

  • Weisen Sie ihren Endpunkten automatisch einen Bestellwert basierend auf der aufgerufenen Reihenfolge zu.

Endpunktrouting in ASP.NET Core:

  • Weist kein Konzept für Routen auf.
  • Stellt keine Bestellgarantien für die Ausführung der Erweiterbarkeit bereit, werden alle Endpunkte gleichzeitig verarbeitet.

Wenn Sie die Protokollierung aktivieren, erfahren Sie, wie die integrierten Routingimplementierungen (z.B. Route) Zuordnungen für Anforderungen ermitteln.

Das Attributrouting wird später in diesem Dokument erläutert.

Mehrere herkömmliche Routen

Mehrere herkömmliche Routen können konfiguriert werden, indem sie weitere Anrufe hinzufügen MapControllerRoute und MapAreaControllerRoute. Dadurch können mehrere Konventionen definiert werden oder herkömmliche Routen hinzugefügt werden, die einer bestimmten Aktion zugeordnet sind, z. B.:

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

Die blog Route im vorherigen Code ist eine dedizierte herkömmliche Route. Es wird als dedizierte herkömmliche Route bezeichnet, da:

Da controller und action nicht in der Routenvorlage "blog/{*article}" als Parameter angezeigt werden:

  • Sie können nur über die Standardwerte { controller = "Blog", action = "Article" }verfügen.
  • Diese Route entspricht immer der Aktion BlogController.Article.

/Blog, /Blog/Articleund /Blog/{any-string} sind die einzigen URL-Pfade, die mit der Blogroute übereinstimmen.

Für das vorherige Beispiel gilt Folgendes:

  • blog route hat eine höhere Priorität für Übereinstimmungen als die default Route, da sie zuerst hinzugefügt wird.
  • Ist ein Beispiel für das Slug-Formatrouting, bei dem es typisch ist, einen Artikelnamen als Teil der URL zu haben.

Warnung

In ASP.NET Core wird das Routing nicht:

  • Definieren Sie ein Konzept, das als Route bezeichnet wird. UseRouting fügt der Middlewarepipeline einen Routenabgleich hinzu. In UseRouting der Middleware werden die in der App definierten Endpunkte untersucht und die beste Endpunkt-Übereinstimmung basierend auf der Anforderung ausgewählt.
  • Stellen Sie Garantien für die Ausführungsreihenfolge der Erweiterbarkeit wie IRouteConstraint oder IActionConstraint.

Siehe Routing für Referenzmaterial zum Routing.

Herkömmlicher Routingauftrag

Herkömmliches Routing entspricht nur einer Kombination aus Aktion und Controller, die von der App definiert werden. Dies soll die Fälle vereinfachen, in denen herkömmliche Routen überlappen. Hinzufügen von Routen mithilfe MapControllerRoutevon , MapDefaultControllerRouteund MapAreaControllerRoute weisen Sie ihren Endpunkten automatisch einen Bestellwert basierend auf der aufgerufenen Reihenfolge zu. Übereinstimmungen aus einer Route, die früher angezeigt wird, haben eine höhere Priorität. Beim herkömmlichen Routing ist die Reihenfolge wichtig. Im Allgemeinen sollten Routen mit Gebieten früher platziert werden, da sie spezifischer als Routen ohne Einen Bereich sind. Dedizierte herkömmliche Routen mit Fangen-all-Routenparametern können eine {*article}Route zu gierig machen, d. h. sie entspricht URLs, die Sie von anderen Routen übereinstimmen möchten. Legen Sie die gierigen Routen später in der Routetabelle fest, um gierige Übereinstimmungen zu verhindern.

Warnung

Ein catch-all-Parameter kann aufgrund eines Fehlers beim Routing nicht ordnungsgemäß mit Routen übereinstimmen. Apps, die von diesem Fehler betroffen sind, weisen die folgenden Merkmale auf:

  • Eine catch-all-Route, zum Beispiel {**slug}"
  • Die catch-all-Route kann nicht mit Anforderungen abgeglichen werden, die abgeglichen werden sollen.
  • Durch das Entfernen anderer Routen funktioniert die catch-all-Route.

Weitere Beispiele zu diesem Fehler finden Sie in den GitHub-Issues 18677 und 16579.

Eine Opt-in-Behebung für diesen Fehler ist im .NET Core 3.1.301 SDK und höher enthalten. Der folgende Code legt einen internen Switch fest, mit dem dieser Fehler behoben wird:

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

Auflösen von mehrdeutigen Aktionen

Wenn zwei Endpunkte durch Routing übereinstimmen, muss das Routing eine der folgenden Aktionen ausführen:

  • Wählen Sie den besten Kandidaten aus.
  • Löst eine Ausnahme aus.

Beispiel:

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

Der vorherige Controller definiert zwei Aktionen, die übereinstimmen:

  • Der URL-Pfad /Products33/Edit/17
  • Weiterleiten von Daten { controller = Products33, action = Edit, id = 17 }.

Dies ist ein typisches Muster für MVC-Controller:

  • Edit(int) zeigt ein Formular an, um ein Produkt zu bearbeiten.
  • Edit(int, Product) verarbeitet das gepostete Formular.

So beheben Sie die richtige Route:

  • Edit(int, Product) wird ausgewählt, wenn die Anforderung ein HTTP POSTist.
  • Edit(int) wird ausgewählt, wenn das HTTP-Verb etwas anderes ist. Edit(int) wird im Allgemeinen via GETaufgerufen.

Das HttpPostAttribute, [HttpPost]wird zum Routing bereitgestellt, damit er basierend auf der HTTP-Methode der Anforderung auswählen kann. Dies HttpPostAttribute macht Edit(int, Product) eine bessere Übereinstimmung als Edit(int).

Es ist wichtig, die Rolle von Attributen wie HttpPostAttribute. Ähnliche Attribute werden für andere HTTP-Verben definiert. In herkömmlichem Routing ist es üblich, dass Aktionen denselben Aktionsnamen verwenden, wenn sie Teil eines Showformulars sind, Formularworkflow übermitteln. Weitere Informationen finden Sie unter "Überprüfen der beiden Bearbeitungsaktionsmethoden".

Wenn Routing keinen besten Kandidaten auswählen kann, wird ein AmbiguousMatchException Wurf ausgelöst, der die mehreren übereinstimmend Endpunkte auflisten kann.

Herkömmliche Routennamen

Die Zeichenfolgen und "default" in den folgenden Beispielen "blog" sind herkömmliche Routennamen:

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

Die Routennamen geben der Route einen logischen Namen. Die benannte Route kann für die URL-Generation verwendet werden. Die Verwendung einer benannten Route vereinfacht die URL-Erstellung, wenn die Reihenfolge von Routen die URL-Generation kompliziert machen könnte. Routennamen müssen eine eindeutige Anwendung breit sein.

Routennamen:

  • Haben keine Auswirkungen auf die URL-Übereinstimmung oder die Behandlung von Anforderungen.
  • Wird nur für die URL-Generation verwendet.

Das Routennamenkonzept wird in Routing als IEndpointNameMetadata dargestellt. Name und Endpunktname der Begriffe:

  • Sind austauschbar.
  • Welche in Dokumentation und Code verwendet wird, hängt davon ab, welche API beschrieben wird.

Attributrouting für REST-APIs

REST-APIs sollten Attributrouting verwenden, um die Funktionalität der App als Gruppe von Ressourcen zu modellieren, in denen Vorgänge durch HTTP-Verben dargestellt werden.

Beim Attributrouting werden Aktionen mithilfe von Attributen direkt Routenvorlagen zugeordnet. Der folgende Code ist typisch für eine REST-API und wird im nächsten Beispiel verwendet:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Im vorherigen Code wird aufgerufen, MapControllers um Attributroutencontroller zuzuordnen.

Im folgenden Beispiel:

  • HomeController entspricht einer Reihe von URLs ähnlich wie der Standardroute {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);
    }
}

Die HomeController.Index Aktion wird für eine der URL-Pfade /, , /Home, /Home/Indexoder /Home/Index/3.

In diesem Beispiel wird ein wichtiger Programmierunterschied zwischen Attributrouting und herkömmlichem Routing hervorgehoben. Attributrouting erfordert mehr Eingabe, um eine Route anzugeben. Die herkömmliche Standardroute behandelt Routen genauer. Das Attributrouting ermöglicht jedoch die genaue Kontrolle, welche Routenvorlagen für jede Aktion gelten.

Bei Attributrouting spielen die Controller- und Aktionsnamen keine Rolle, in der eine Aktion übereinstimmt, es sei denn, die Token-Ersatz wird verwendet. Das folgende Beispiel entspricht den gleichen URLs wie im vorherigen Beispiel:

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

Der folgende Code verwendet Token-Ersatz für action und 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();
    }
}

Der folgende Code gilt [Route("[controller]/[action]")] für den 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();
    }
}

Im vorherigen Code müssen die Index Methodenvorlagen vorab oder ~/ an die Routenvorlagen weitergeleitet werden/. Routenvorlagen, die auf eine Aktion angewendet werden, die mit einem / oder ~/ beginnen, können nicht mit Routenvorlagen kombiniert werden, die auf den Controller angewendet werden.

Weitere Informationen zur Auswahl der Routenvorlage finden Sie unter "Route"-Vorlage .

Reservierte Routingnamen

Die folgenden Schlüsselwörter sind reservierte Parameternamen bei Verwendung von Controllern oder Razor Seiten:

  • action
  • area
  • controller
  • handler
  • page

Die Verwendung als Routenparameter mit Attributrouting ist ein gängiger page Fehler. Dies führt zu inkonsistenten und verwirrenden Verhalten bei der URL-Generation.

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

Die speziellen Parameternamen werden von der URL-Generation verwendet, um festzustellen, ob sich ein URL-Generationsvorgang auf eine Razor Seite oder einen Controller bezieht.

  • Die folgenden Schlüsselwörter sind im Kontext einer Razor-Ansicht oder einer Razor-Seite reserviert:

  • page

  • using

  • namespace

  • inject

  • section

  • inherits

  • model

  • addTagHelper

  • removeTagHelper

Diese Schlüsselwörter sollten nicht für Verknüpfungsgenerationen, modellgebundene Parameter oder Eigenschaften auf oberster Ebene verwendet werden.

HTTP-Verbvorlagen

ASP.NET Core hat die folgenden HTTP-Verbvorlagen:

Routenvorlagen

ASP.NET Core verfügt über die folgenden Routenvorlagen:

Attributrouting mit Http-Verbattributen

Berücksichtigen Sie den folgenden 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);
    }
}

Für den Code oben gilt:

  • Jede Aktion enthält das [HttpGet] Attribut, das nur die Übereinstimmung mit HTTP GET-Anforderungen beschränkt.
  • Die GetProduct Aktion enthält die "{id}" Vorlage, daher id wird an die Vorlage auf dem "api/[controller]" Controller angefügt. Die Methodenvorlage lautet "api/[controller]/"{id}"". Daher stimmt diese Aktion nur mit GET-Anforderungen für das Formular/api/test2/xyz,/api/test2/123/api/test2/{any string} usw. überein.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • Die GetIntProduct Aktion enthält die "int/{id:int}") Vorlage. Der :int Teil der Vorlage beschränkt die id Routenwerte auf Zeichenfolgen, die in eine ganze Zahl konvertiert werden können. Eine GET-Anforderung an /api/test2/int/abc:
    • Entspricht dieser Aktion nicht.
    • Gibt einen Fehler "404 Nicht gefunden" zurück .
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • Die GetInt2Product Aktion enthält {id} in der Vorlage, beschränkt sich jedoch nicht auf id Werte, die in eine ganze Zahl konvertiert werden können. Eine GET-Anforderung an /api/test2/int2/abc:
    • Entspricht dieser Route.
    • Die Modellbindung kann nicht in eine ganze Zahl konvertiert abc werden. Der id Parameter der Methode ist ganze Zahl.
    • Gibt eine ungültige Anforderung von 400 zurück, da die Modellbindung nicht in eine ganze Zahl konvertiert werdenabc konnte.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

Attributrouting kann Attribute wie HttpMethodAttribute , HttpPostAttributeHttpPutAttribute, und HttpDeleteAttribute. Alle HTTP-Verbattribute akzeptieren eine Routenvorlage. Das folgende Beispiel zeigt zwei Aktionen, die mit derselben Routenvorlage übereinstimmen:

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

Verwenden des URL-Pfads /products3:

  • Die MyProductsController.ListProducts Aktion wird ausgeführt, wenn das HTTP-Verb ist GET.
  • Die MyProductsController.CreateProduct Aktion wird ausgeführt, wenn das HTTP-Verb ist POST.

Beim Erstellen einer REST-API ist es selten, dass Sie für eine Aktionsmethode verwenden [Route(...)] müssen, da die Aktion alle HTTP-Methoden akzeptiert. Es ist besser, das spezifischere HTTP-Verb-Attribut zu verwenden, um genau zu sein, was Ihre API unterstützt. REST-API-Clients sollten wissen, welche Pfade und HTTP-Verben bestimmten logischen Operationen entsprechen.

REST-APIs sollten Attributrouting verwenden, um die Funktionalität der App als Eine Reihe von Ressourcen zu modellieren, in denen Vorgänge durch HTTP-Verben dargestellt werden. Dies bedeutet, dass viele Vorgänge, z. B. GET und POST in derselben logischen Ressource dieselbe URL verwenden. Das Attributrouting bietet eine Ebene der Steuerung, die für einen sorgfältigen Entwurf des öffentlichen Endpunktlayouts einer API erforderlich ist.

Da eine Attributroute für eine bestimmte Aktion gilt, ist es einfach, Parameter als Teil der Routenvorlagendefinition erforderlich festzulegen. Im folgenden Beispiel id ist als Teil des URL-Pfads erforderlich:

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

Die Products2ApiController.GetProduct(int) Aktion:

  • Wird mit URL-Pfad wie z. B. ausgeführt /products2/3
  • Wird nicht mit dem URL-Pfad /products2ausgeführt.

Das [Consumes]-Attribut ermöglicht es einer Aktion, die unterstützten Anforderungsinhaltstypen einzuschränken. Weitere Informationen finden Sie unter Definieren unterstützter Anforderungsinhaltstypen mit dem Attribut "Consumes".

Eine vollständige Beschreibung und Routenvorlagen und dazugehörige Optionen finden Sie unter Routing in ASP.NET Core.

Weitere Informationen finden [ApiController]Sie unter "ApiController"-Attribut.

Routenname

Der folgende Code definiert einen Routennamen von Products_List:

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

Routennamen können verwendet werden, um basierend auf einer bestimmten Route eine URL zu generieren. Routennamen:

  • Haben keine Auswirkungen auf das URL-Abgleichsverhalten des Routings.
  • Wird nur für die URL-Generation verwendet.

Routennamen müssen anwendungsweit eindeutig sein.

Kontrast zum vorherigen Code mit der herkömmlichen Standardroute, die den id Parameter als optional ({id?}) definiert. Die Möglichkeit, apIs genau anzugeben, hat Vorteile, z. B. das Zulassen /products und /products/5 Senden an unterschiedliche Aktionen.

Kombinieren von Attributrouten

Um Attributrouting weniger repetitiv zu gestalten, werden Routenattribute auf dem Controller mit Routenattributen auf den einzelnen Aktionen kombiniert. Alle auf dem Controller definierten Routenvorlagen werden den Routenvorlagen auf den Aktionen vorangestellt. Wenn Routenattribute auf dem Controller platziert werden, verwenden alle Aktionen im Controller Attributrouting.

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

Im vorherigen Beispiel:

  • Der URL-Pfad /products kann übereinstimmen ProductsApi.ListProducts
  • Der URL-Pfad /products/5 kann übereinstimmen ProductsApi.GetProduct(int).

Beide Aktionen stimmen nur mit HTTP GET überein, da sie mit dem [HttpGet] Attribut gekennzeichnet sind.

Routenvorlagen, die auf eine Aktion angewendet werden, die mit einem / oder ~/ beginnen, können nicht mit Routenvorlagen kombiniert werden, die auf den Controller angewendet werden. Im folgenden Beispiel wird eine Reihe von URL-Pfaden übereinstimmen, die der Standardroute ähneln.

[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 der folgenden Tabelle werden die [Route] Attribute im vorherigen Code erläutert:

Attribut Kombiniert mit [Route("Home")] Definiert die Route-Vorlage
[Route("")] Ja "Home"
[Route("Index")] Ja "Home/Index"
[Route("/")] Nein ""
[Route("About")] Ja "Home/About"

Attributroutenreihenfolge

Das Routing erstellt eine Struktur und entspricht allen Endpunkten gleichzeitig:

  • Die Routeneinträge verhalten sich so, als ob sie in einer idealen Reihenfolge platziert werden.
  • Die spezifischsten Routen haben die Möglichkeit, vor den allgemeineren Routen auszuführen.

Beispielsweise ist eine Attributroute wie blog/search/{topic} eine Attributroute spezifischer als eine Attributroute wie blog/{*article}. Die blog/search/{topic} Route hat standardmäßig höhere Priorität, da sie spezifischer ist. Mit herkömmlichem Routing ist der Entwickler für das Platzieren von Routen in der gewünschten Reihenfolge verantwortlich.

Attributrouten können eine Bestellung mithilfe der Order Eigenschaft konfigurieren. Alle bereitgestellten Routingattribute des Frameworks umfassen Order . Routen werden entsprechend einer aufsteigenden Reihenfolge der Order-Eigenschaft verarbeitet. Die Standardreihenfolge ist 0. Festlegen einer Route mithilfe von Order = -1 Ausführungsläufen vor Routen, die keine Reihenfolge festlegen. Festlegen einer Route mithilfe von Order = 1 Ausführung nach der Standardroutenreihenfolge.

Vermeiden Sie je nach Order. Wenn der URL-Leerraum einer App explizite Reihenfolgewerte erfordert, die ordnungsgemäß weitergeleitet werden sollen, ist es wahrscheinlich auch für Clients verwirrend. Im Allgemeinen wählt das Attributrouting die richtige Route mit URL-Übereinstimmung aus. Wenn die für die URL-Generation verwendete Standardreihenfolge nicht funktioniert, ist die Verwendung eines Routennamens als Außerkraftsetzung in der Regel einfacher als das Anwenden der Order Eigenschaft.

Berücksichtigen Sie die folgenden beiden Controller, die beide den Routenabgleich /homedefinieren:

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

Das Anfordern /home mit dem vorherigen Code löst eine Ausnahme wie folgt aus:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

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

Durch das Hinzufügen Order zu einem der Routenattribute wird die Mehrdeutigkeit aufgelöst:

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

Führen Sie mit dem vorherigen Code /home den HomeController.Index Endpunkt aus. Um zum MyDemoController.MyIndex, anfordern /home/MyIndex. Hinweis:

  • Der vorherige Code ist ein Beispiel oder ein schlechtes Routingdesign. Es wurde verwendet, um die Order Eigenschaft zu veranschaulichen.
  • Die Order Eigenschaft löst nur die Unklarheit auf, diese Vorlage kann nicht übereinstimmen. Es wäre besser, die [Route("Home")] Vorlage zu entfernen.

Siehe Razor Seitenrouten- und App-Konventionen: Route order for information on route order with Razor Pages.

In einigen Fällen wird ein HTTP 500-Fehler mit mehrdeutigen Routen zurückgegeben. Verwenden Sie die Protokollierung , um zu sehen, welche Endpunkte dies AmbiguousMatchExceptionverursacht haben.

Token-Ersatz in Routenvorlagen [Controller], [Aktion], [Bereich]

Die Attributrouten unterstützen die Tokenbesetzung durch schließendes Token in quadratische Klammern ([, ]). Die Token [action], [area]und [controller] werden durch die Werte des Aktionsnamens, des Bereichsnamens und des Controllernamens aus der Aktion ersetzt, in der die Route definiert ist:

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

Für den Code oben gilt:

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

Die Tokenersetzung tritt im letzten Schritt der Erstellung von Attributrouten auf. Im vorhergehenden Beispiel verhält sich dasselbe wie der folgende 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);
    }
}

Wenn Sie dies in einer anderen Sprache als Englisch lesen, informieren Sie uns in diesem GitHub Diskussionsproblem, wenn Sie die Codekommentare in Ihrer Muttersprache sehen möchten.

Attributrouten können auch mit Vererbung kombiniert werden. Dies ist leistungsfähig, kombiniert mit Token-Ersatz. Tokenersetzung gilt auch für Routennamen, die durch Attributrouten definiert werden. [Route("[controller]/[action]", Name="[controller]_[action]")]generiert einen eindeutigen Routennamen für jede Aktion:

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

Um mit dem Literaltokentrennzeichen [ übereinzugleichen oder ]es zu escape, indem Sie das Zeichen ([[ oder ]]) wiederholen.

Verwenden eines Parametertransformators zum Anpassen der Tokenersetzung

Die Tokenersetzung kann mit einem Parametertransformator angepasst werden. Ein Parametertransformator implementiert IOutboundParameterTransformer und wandelt den Wert der Parameter um. Beispielsweise ändert ein benutzerdefinierter SlugifyParameterTransformer Parametertransformor den SubscriptionManagement Routenwert 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();
    }
}

Die RouteTokenTransformerConvention ist eine Anwendungsmodellkonvention, die Folgendes ausführt:

  • Wendet einen angegebenen Parametertransformator auf alle Attributrouten in der App an.
  • Passt die Tokenwerte für Attributrouten bei ihrer Ersetzung an.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Die vorhergehende ListAll Methode entspricht /subscription-management/list-all.

Dies RouteTokenTransformerConvention ist als Option registriert:

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

Weitere Informationen finden Sie unter MDN-Webdokumenten in Slug für die Definition von Slug.

Warnung

Übergeben Sie ein Timeout, wenn Sie System.Text.RegularExpressions zum Verarbeiten nicht vertrauenswürdiger Eingaben verwenden. Ein böswilliger Benutzer kann Eingaben für RegularExpressions angeben, um einen Denial-of-Service-Angriff durchzuführen. ASP.NET Core-Framework-APIs, die RegularExpressions verwenden, übergeben ein Timeout.

Mehrere Attributrouten

Attributrouting unterstützt das Definieren mehrerer Routen, die zu derselben Aktion führen. Dies wird am häufigsten beim Imitieren des Verhaltens der herkömmlichen Standardroute verwendet. Im folgenden Beispiel wird dies gezeigt:

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

Das Platzieren mehrerer Routenattribute auf dem Controller bedeutet, dass jeder mit jedem der Routenattribute auf den Aktionsmethoden kombiniert:

[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-Verbrouteneinschränkungen implementieren .IActionConstraint

Wenn mehrere Routingattribute, die implementiert IActionConstraint werden, in einer Aktion platziert werden:

  • Jede Aktionseinschränkung kombiniert die Routevorlage, die auf den Controller angewendet wird.
[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();
    }
}

Die Verwendung mehrerer Routen für Aktionen scheint nützlich und leistungsfähig zu sein, es ist besser, den URL-Speicherplatz Ihrer App einfach und gut definiert zu halten. Verwenden Sie mehrere Routen nur bei Bedarf, um vorhandene Clients zu unterstützen.

Angeben von optionalen Attributroutenparametern, Standardwerten und Einschränkungen

Attributrouten unterstützen dieselbe Inline-Syntax wie herkömmliche Routen, um optionale Parameter, Standardwerte und Einschränkungen anzugeben.

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

Wendet im vorherigen Code eine Routeeinschränkung [HttpPost("product14/{id:int}")] an. Die Products14Controller.ShowProduct Aktion wird nur mit URL-Pfaden wie /product14/3. Der Abschnitt {id:int} "Route"-Vorlage beschränkt das Segment nur auf ganze Zahlen.

Eine ausführliche Beschreibung der Syntax der Routenvorlage finden Sie unter Routenvorlagenreferenz.

Benutzerdefinierte Routenattribute mithilfe von IRouteTemplateProvider

Alle Routenattribute implementieren IRouteTemplateProvider. Die ASP.NET Core Laufzeit:

  • Sucht nach Attributen für Controllerklassen und Aktionsmethoden, wenn die App gestartet wird.
  • Verwendet die Attribute, die implementiert werden, IRouteTemplateProvider um die anfängliche Gruppe von Routen zu erstellen.

Implementieren Sie IRouteTemplateProvider , um benutzerdefinierte Routenattribute zu definieren. Jeder IRouteTemplateProvider lässt Sie eine einzelne Route mit einer benutzerdefinierten Routenvorlage, Reihenfolge und einem benutzerdefinierten Namen definieren:

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

Die vorherige Get Methode gibt Order = 2, Template = api/MyTestApizurück .

Verwenden des Anwendungsmodells zum Anpassen von Attributrouten

Das Anwendungsmodell:

  • Ist ein Objektmodell, das beim Start in Program.cserstellt wurde.
  • Enthält alle Metadaten, die von ASP.NET Core zum Weiterleiten und Ausführen der Aktionen in einer App verwendet werden.

Das Anwendungsmodell enthält alle Daten, die aus Routenattributen gesammelt werden. Die Daten aus Routenattributen werden von der IRouteTemplateProvider Implementierung bereitgestellt. Konventionen:

  • Kann geschrieben werden, um das Anwendungsmodell zu ändern, um das Verhalten des Routings anzupassen.
  • Werden beim Start der App gelesen.

In diesem Abschnitt wird ein grundlegendes Beispiel zum Anpassen des Routings mithilfe des Anwendungsmodells dargestellt. Der folgende Code macht Routen ungefähr mit der Ordnerstruktur des Projekts zusammen.

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

Der folgende Code verhindert, dass die Konvention auf Controller angewendet wird, die namespace Attribut weitergeleitet werden:

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

Beispielsweise wird der folgende Controller nicht verwendet NamespaceRoutingConvention:

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

Die NamespaceRoutingConvention.Apply-Methode:

  • Macht nichts, wenn der Controller attributiert wird.
  • Legt die Controllervorlage basierend auf dem namespace, wobei die Basis namespace entfernt wurde.

Dies NamespaceRoutingConvention kann 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();

Berücksichtigen Sie beispielsweise den folgenden 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}");
        }
    }
}

Für den Code oben gilt:

  • Die Basis namespace ist My.Application.
  • Der vollständige Name des vorherigen Controllers ist My.Application.Admin.Controllers.UsersController.
  • Die NamespaceRoutingConvention Vorlage "Controller" wird auf Admin/Controllers/Users/[action]/{id?"." festgelegt.

Dies NamespaceRoutingConvention kann auch als Attribut auf einem Controller angewendet werden:

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

Gemischtes Routing: Attributrouting vs. herkömmliches Routing

ASP.NET Core Apps können die Verwendung herkömmlicher Routing- und Attributroutings kombinieren. In der Regel werden herkömmliche Routen für Controller verwendet, die für Browser-HTML-Seiten gedacht sind, und das Attributrouting für Controller für REST-APIs.

Aktionen werden entweder herkömmlich oder über Attribute zugeordnet, d.h., dass eine Route auf dem Controller oder der Aktion platziert wird. Aktionen, die Attributrouten definieren, können nicht über die herkömmliche Routen und umgekehrt erreicht werden. Jedes Route-Attribut des Controllers macht alle Aktionen im Controller-Attribut weitergeleitet.

Attributrouting und herkömmliches Routing verwenden das gleiche Routingmodul.

URL-Generation und Umgebungswerte

Apps können Routing-URL-Generationsfeatures verwenden, um URL-Links zu Aktionen zu generieren. Das Generieren von URLs entfernt hart codierende URLs, wodurch Code robuster und wartungsfähiger wird. Dieser Abschnitt konzentriert sich auf die von MVC bereitgestellten URL-Generationsfeatures und deckt nur Grundlagen der Funktionsweise der URL-Generation ab. Eine detaillierte Beschreibung der URL-Generierung finden Sie unter Routing in ASP.NET Core.

Die IUrlHelper Schnittstelle ist das zugrunde liegende Element der Infrastruktur zwischen MVC und Routing für die URL-Generation. Eine Instanz von IUrlHelper ist über die Url Eigenschaft in Controllern, Ansichten und Ansichtskomponenten verfügbar.

Im folgenden Beispiel wird die Schnittstelle über die IUrlHelperController.Url Eigenschaft verwendet, um eine URL zu einer anderen Aktion zu generieren.

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

Wenn die App die standardbasierte Route verwendet, ist der Wert der url Variable die URL-Pfadzeichenfolge /UrlGeneration/Destination. Dieser URL-Pfad wird durch Routing erstellt, indem Folgendes kombiniert wird:

  • Die Routenwerte aus der aktuellen Anforderung, die als Umgebungswerte bezeichnet werden.
  • Die an diese Werte übergebenen Url.Action und substituieren diese Werte in die Routenvorlage:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

Der Wert eines jeden Routenparameters wird in der Routenvorlage durch die entsprechenden Namen mit den Werten und Umgebungswerten ersetzt. Ein Routenparameter, der keinen Wert hat, kann:

  • Verwenden Sie einen Standardwert, wenn er einen hat.
  • Werden Sie übersprungen, wenn es optional ist. Beispiel: Die id aus der Routenvorlage {controller}/{action}/{id?}.

Fehler bei der URL-Generierung, wenn ein erforderlicher Routenparameter keinen entsprechenden Wert hat. Wenn die URL-Generierung für eine Route fehlschlägt, wird die nächste Route ausprobiert, bis alle Routen getestet wurden oder eine Übereinstimmung gefunden wurde.

Im vorherigen Beispiel wird Url.Action davon ausgegangen , dass herkömmliches Routing ausgeführt wird. Die URL-Generation funktioniert ähnlich mit dem Attributrouting, obwohl die Konzepte unterschiedlich sind. Mit herkömmlichem Routing:

  • Die Routenwerte werden verwendet, um eine Vorlage zu erweitern.
  • Die Routenwerte für controller diese Vorlage und action werden normalerweise in dieser Vorlage angezeigt. Dies funktioniert, da die URLs, die durch Routing übereinstimmen, einer Konvention entsprechen.

Im folgenden Beispiel wird attributweiterleitung verwendet:

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

Die Source Aktion im vorherigen Code generiert custom/url/to/destination.

LinkGeneratorwurde in ASP.NET Core 3.0 als Alternative zu IUrlHelper. LinkGenerator bietet ähnliche, aber flexiblere Funktionen. Jede Methode enthält IUrlHelper auch eine entsprechende Familie von Methoden LinkGenerator .

Generieren von URLs nach Aktionsnamen

Url.Action, LinkGenerator.GetPathByAction und alle zugehörigen Überladungen sollen den Zielendpunkt generieren, indem ein Controllername und Aktionsname angegeben wird.

Bei Verwendung Url.Actionwerden die aktuellen Routenwerte für controller die action Laufzeit bereitgestellt:

  • Der Wert und controlleraction sind Teil von Umgebungswerten und Werten. Die Methode Url.Action verwendet immer die aktuellen Werte action und controller generiert einen URL-Pfad, der an die aktuelle Aktion weitergeleitet wird.

Routing versucht, die Werte in Umgebungswerten zu verwenden, um Informationen auszufüllen, die beim Generieren einer URL nicht bereitgestellt wurden. Betrachten Sie eine Route wie {a}/{b}/{c}/{d} bei Umgebungswerten { a = Alice, b = Bob, c = Carol, d = David }:

  • Das Routing verfügt über genügend Informationen, um eine URL ohne zusätzliche Werte zu generieren.
  • Routing verfügt über genügend Informationen, da alle Routenparameter einen Wert haben.

Wenn der Wert { d = Donovan } hinzugefügt wird:

  • Der Wert { d = David } wird ignoriert.
  • Der generierte URL-Pfad ist Alice/Bob/Carol/Donovan.

Warnung: URL-Pfade sind hierarchisch. Wenn der Wert { c = Cheryl } im vorherigen Beispiel hinzugefügt wird:

  • Beide Werte { c = Carol, d = David } werden ignoriert.
  • Es gibt keinen Wert mehr, d und die URL-Generation schlägt fehl.
  • Die gewünschten Werte c und d müssen angegeben werden, um eine URL zu generieren.

Möglicherweise erwarten Sie, dass dieses Problem mit der Standardroute erreicht wird {controller}/{action}/{id?}. Dieses Problem ist in der Praxis selten, da Url.Action immer explizit ein und action ein controller Wert angegeben wird.

Mehrere Überladungen von Url.Action führen ein Routingwertobjekt aus, um Werte für andere Routenparameter als controller und action. Das Route values-Objekt wird häufig mit id. Beispiel: Url.Action("Buy", "Products", new { id = 17 }). Das Route values-Objekt:

  • In der Regel handelt es sich um ein Objekt anonymer Art.
  • Kann ein IDictionary<> oder ein POCO sein).

Alle zusätzlichen Routenwerte, die keinen Routenparametern zugeordnet sind, werden in der Abfragezeichenfolge platziert.

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

Der vorherige Code generiert /Products/Buy/17?color=red.

Der folgende Code generiert eine absolute URL:

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

Verwenden Sie eine der folgenden Optionen, um eine absolute URL zu erstellen:

  • Eine Überladung, die eine protocol. Beispiel: der vorherige Code.
  • LinkGenerator.GetUriByAction, das standardmäßig absolute URIs generiert.

Generieren von URLs nach Route

Der vorherige Code veranschaulichte das Generieren einer URL durch Übergeben des Controllers und des Aktionsnamens. IUrlHelper stellt auch die Url.RouteUrl-Familie von Methoden bereit. Diese Methoden ähneln Url.Action, aber sie kopieren nicht die aktuellen Werte action und controller in die Routenwerte. Die häufigste Verwendung von Url.RouteUrl:

  • Gibt einen Routennamen an, der die URL generiert.
  • In der Regel wird kein Controller- oder Aktionsname angegeben.
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();
    }

Die folgende Razor Datei generiert einen HTML-Link zum Destination_Route:

<h1>Test Links</h1>

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

Generieren von URLs in HTML und Razor

IHtmlHelperstellt die HtmlHelper Methoden Html.BeginForm und Html.ActionLink bereit, um elemente zu generieren und <a> zu generieren<form>. Diese Methoden verwenden die Url.Action-Methode , um eine URL zu generieren und ähnliche Argumente anzunehmen. Die Url.RouteUrl-Begleiter für HtmlHelper sind Html.BeginRouteForm und Html.RouteLink, die ähnliche Funktionen aufweisen.

Taghilfsprogramme generieren URLs mit den Taghilfsprogrammen form und <a>. Beide implementieren mit IUrlHelper. Weitere Informationen finden Sie unter Taghilfen in Formularen .

In Ansichten ist IUrlHelper über die Url-Eigenschaft für jede Ad-hoc-URL-Generierung verfügbar, die keine der oben genannten ist.

URL-Generation in Aktionsergebnissen

Die vorherigen Beispiele zeigen die Verwendung IUrlHelper in einem Controller. Die häufigste Verwendung in einem Controller besteht darin, eine URL als Teil eines Aktionsergebnisses zu generieren.

Die Basisklassen ControllerBase und Controller stellen Hilfsmethoden für Aktionsergebnisse bereit, die auf eine andere Aktionen verweisen. Eine typische Verwendung besteht darin, nach der Annahme der Benutzereingabe umzuleiten:

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

Die Aktionsergebnisse-Factorymethoden wie z RedirectToAction . B. und CreatedAtAction folgen einem ähnlichen Muster wie den Methoden für IUrlHelper.

Sonderfall für dedizierte herkömmliche Routen

Herkömmliches Routing kann eine spezielle Art von Routendefinition verwenden, die als dedizierte konventionelle Route bezeichnet wird. Im folgenden Beispiel ist die benannte blog Route eine dedizierte konventionelle Route:

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

Generiert mithilfe der vorherigen Routendefinitionen Url.Action("Index", "Home") den URL-Pfad / mithilfe der default Route, aber warum? Man könnte meinen, dass die Routenwerte { controller = Home, action = Index } ausreichen würden, um eine URL mithilfe von blog zu generieren, und das Ergebnis wäre /blog?action=Index&controller=Home.

Dedizierte konventionelle Routen basieren auf einem speziellen Verhalten von Standardwerten, die keinen entsprechenden Routenparameter haben, der verhindert, dass die Route mit der URL-Generation zu gierig ist. In diesem Fall sind die Standardwerte { controller = Blog, action = Article }, und weder controller noch action werden als Routenparameter verwendet. Wenn das Routing die URL-Generierung ausführt, müssen die angegebenen Werte mit den Standardwerten übereinstimmen. Fehler bei der URL-Generierung, blog da die Werte { controller = Home, action = Index } nicht übereinstimmen { controller = Blog, action = Article }. Routing greift dann wieder auf default zurück, was erfolgreich ausgeführt wird.

Bereiche

Bereiche sind ein MVC-Feature, das verwendet wird, um verwandte Funktionen in einer Gruppe als separates zu organisieren:

  • Routingnamespace für Controlleraktionen.
  • Ordnerstruktur für Ansichten.

Mithilfe von Bereichen kann eine App mehrere Controller mit demselben Namen haben, solange sie unterschiedliche Bereiche haben. Mithilfe von Bereichen wird außerdem eine Hierarchie erstellt, damit das Routing durch Hinzufügen eines anderen Routenparameters ausgeführt werden kann: area zu controller und action. In diesem Abschnitt wird erläutert, wie routing mit Bereichen interagiert. Details dazu, wie Bereiche mit Ansichten verwendet werden, finden Sie in Den Bereichen.

Im folgenden Beispiel wird MVC so konfiguriert, dass die herkömmliche Standardroute und eine area Route für eine area benannte BlogRoute verwendet wird:

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

Im vorherigen Code wird aufgerufen, MapAreaControllerRoute um die "blog_route". Der zweite Parameter ist "Blog"der Bereichsname.

Wenn ein URL-Pfad wie folgt /Manage/Users/AddUserübereinstimmen, generiert die "blog_route" Route die Routenwerte { area = Blog, controller = Users, action = AddUser }. Der area Routenwert wird durch einen Standardwert für area. Die von MapAreaControllerRoute ihnen erstellte Route entspricht folgendem:

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

MapAreaControllerRoute erstellt mit einem Standardwert und einer Einschränkung für area sowie mit dem bereitgestellten Bereichsnamen (in diesem Fall Blog) eine Route. Der Standardwert stellt sicher, dass die Route immer { area = Blog, ... } erzeugt, die Einschränkung erfordert den Wert { area = Blog, ... } für URL-Generierung.

Beim herkömmlichen Routing ist die Reihenfolge wichtig. Im Allgemeinen sollten Routen mit Gebieten früher platziert werden, da sie spezifischer als Routen ohne Einen Bereich sind.

Mit dem vorherigen Beispiel entsprechen die Routenwerte { area = Blog, controller = Users, action = AddUser } der folgenden Aktion:

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

Das Attribut [Area] ist, was einen Controller als Teil eines Bereichs angibt. Dieser Controller befindet sich im Blog Bereich. Controller ohne Attribut sind keine Mitglieder eines [Area] Bereichs und stimmen nicht überein, wenn der area Routewert durch Routing bereitgestellt wird. Im folgenden Beispiel kann nur der erste aufgelistete Controller die Routenwerte { area = Blog, controller = Users, action = AddUser } abgleichen.

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

Der Namespace jedes Controllers wird hier für Die Vollständigkeit angezeigt. Wenn die vorherigen Controller denselben Namespace verwendet haben, wird ein Compilerfehler generiert. Klassennamespaces haben keine Auswirkungen auf das MVC-Routing.

Die ersten beiden Controller gehören zu Bereichen und werden können nur abgleichen, wenn ihr jeweiliger Bereichsname vom area-Routenwert bereitgestellt wird. Der dritte Controller gehört keinem Bereich an und kann nur abgleichen, wenn vom Routing kein Wert für area bereitgestellt wird.

Im Hinblick auf das Erkennen keines Werts hat die Abwesenheit des area-Werts dieselben Auswirkungen, wie wenn der Wert für area 0 (null) oder eine leere Zeichenfolge wäre.

Beim Ausführen einer Aktion innerhalb eines Bereichs steht der Routewert area als Umgebungswert zur Verwendung für die URL-Generation zur Verfügung. Das bedeutet, dass Bereiche bei der URL-Generierung wie im folgenden Beispiel dargestellt standardmäßig beständig sind.

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

Der folgende Code generiert eine URL zu /Zebra/Users/AddUser:

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

Aktionsdefinition

Öffentliche Methoden auf einem Controller, außer denen mit dem NonAction-Attribut , sind Aktionen.

Beispielcode

Debugdiagnose

Legen Sie für eine ausführliche Routingdiagnoseausgabe Logging:LogLevel:Microsoft auf Debug fest. Legen Sie in der Entwicklungsumgebung die Protokollebene in appsettings.Development.json fest:

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

ASP.NET Core Controller verwenden die Routing-Middleware, um den URLs eingehender Anforderungen zu entsprechen und sie Aktionen zuzuordnen. Routenvorlagen:

  • Werden in Startcode oder Attributen definiert.
  • Beschreiben Sie, wie URL-Pfade mit Aktionen übereinstimmen.
  • Werden verwendet, um URLs für Links zu generieren. Die generierten Links werden in der Regel in Antworten zurückgegeben.

Aktionen sind entweder konventionell weitergeleitet oder Attributrouten. Wenn Sie eine Route auf dem Controller oder einer Aktion platzieren, wird sie attributgeroutet. Weitere Informationen finden Sie im Abschnitt Gemischtes Routing.

Dieses Dokument hat folgende Eigenschaften:

  • Erläutert die Interaktionen zwischen MVC und Routing:
    • Wie typische MVC-Apps die Verwendung von Routingfeatures verwenden.
    • Deckt beides ab:
    • Weitere Informationen finden Sie unter Routing für erweiterte Routingdetails .
  • bezieht sich auf das standardmäßige Routingsystem, das in ASP.NET Core 3.0 hinzugefügt wurde, als Endpunktrouting bezeichnet. Es ist möglich, Controller mit der vorherigen Version des Routings für Kompatibilitätszwecke zu verwenden. Weitere Anweisungen finden Sie im Migrationshandbuch 2.2-3.0 . Verweisen Sie auf die Version 2.2 dieses Dokuments für Referenzmaterial im Legacy-Routingsystem.

Einrichten einer herkömmlichen Route

Startup.Configure In der Regel weist Code ähnlich wie folgt auf, wenn sie herkömmliches Routing verwenden:

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

Innerhalb des Anrufs wird UseEndpointsverwendet, MapControllerRoute um eine einzelne Route zu erstellen. Die einzelne Route ist die benannte default Route. Die meisten Apps mit Controllern und Ansichten verwenden eine Routenvorlage ähnlich der default Route. REST-APIs sollten Attributrouting verwenden.

Die Routevorlage "{controller=Home}/{action=Index}/{id?}":

  • Entspricht einem URL-Pfad wie /Products/Details/5

  • Extrahiert die Routenwerte { controller = Products, action = Details, id = 5 } , indem sie den Pfad tokenisieren. Die Extraktion von Routenwerten führt zu einer Übereinstimmung, wenn die App über einen Controller namens ProductsController und eine Details Aktion verfügt:

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

    MyDisplayRouteInfo wird von dem NuGet-Paket Rick.Docs.Samples.RouteInfo bereitgestellt und zeigt Routeninformationen an.

  • /Products/Details/5 Das Modell bindet den Wert id = 5 , um den id Parameter auf 5festzulegen. Weitere Details finden Sie unter Modellbindung .

  • {controller=Home}Home definiert als Standardcontroller.

  • {action=Index}Index definiert als Standardaction.

  • Das ? Zeichen in {id?} definiert id als optional.

  • Standardmäßige und optionale Routenparameter müssen nicht im URL-Pfad vorhanden sein, damit es eine Übereinstimmung gibt. Eine ausführliche Beschreibung der Syntax der Routenvorlage finden Sie unter Routenvorlagenreferenz.

  • Entspricht dem URL-Pfad /.

  • Erstellt die Routenwerte { controller = Home, action = Index }.

Die Werte für controller die action Verwendung der Standardwerte. id erzeugt keinen Wert, da kein entsprechendes Segment im URL-Pfad vorhanden ist. / nur übereinstimmen, wenn eine HomeControllerIndex Aktion vorhanden ist:

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

Mithilfe der vorhergehenden Controllerdefinitions- und Routenvorlage wird die HomeController.Index Aktion für die folgenden URL-Pfade ausgeführt:

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

Der URL-Pfad / verwendet die Standardcontroller Home und Index Aktion der Route-Vorlage. Der URL-Pfad /Home verwendet die Standardaktion Index für die Routevorlage.

Mit der Hilfsmethode MapDefaultControllerRoute:

endpoints.MapDefaultControllerRoute();

Ersetzt:

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

Wichtig

Routing wird mithilfe der UseRoutingMapControllerRoute, und MapAreaControllerRoute Middleware konfiguriert. So verwenden Sie Controller:

Herkömmliches Routing

Herkömmliches Routing wird mit Controllern und Ansichten verwendet. Die default-Route:

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

Der vorhergehende ist ein Beispiel für eine herkömmliche Route. Es wird als herkömmliches Routing bezeichnet, da es eine Konvention für URL-Pfade erstellt:

  • Das erste Pfadsegment, , {controller=Home}entspricht dem Controllernamen.
  • Das zweite Segment, , {action=Index}entspricht dem Aktionsnamen .
  • Das dritte Segment {id?} wird für eine optionale id. Das ? In {id?} macht es optional. id wird verwendet, um einer Modellentität zuzuordnen.

Verwenden Sie diese default Route, den URL-Pfad:

  • /Products/List ordnet die ProductsController.List Aktion zu.
  • /Blog/Article/17BlogController.Article bezieht den Parameter in der id Regel auf 17.

Diese Zuordnung:

  • Basiert nur auf den Controller- und Aktionsnamen.
  • Basiert nicht auf Namespaces, Quelldateispeicherorten oder Methodenparametern.

Die Verwendung herkömmlicher Routing mit der Standardroute ermöglicht das Erstellen der App, ohne für jede Aktion ein neues URL-Muster zu erstellen. Für eine App mit CRUD-Formataktionen mit Konsistenz für die URLs für Controller:

  • Hilft beim Vereinfachen des Codes.
  • Macht die Benutzeroberfläche besser vorhersagbar.

Warnung

Der id vorherige Code wird als optional durch die Routevorlage definiert. Aktionen können ohne die optionale ID ausgeführt werden, die als Teil der URL bereitgestellt wird. Im Allgemeinen wirdid die URL ausgelassen:

  • id ist auf 0 die Modellbindung festgelegt.
  • Es wird keine Entität im Datenbankabgleich id == 0gefunden.

Das Attributrouting bietet ein fein zugeschnittenes Steuerelement, um die ID für einige Aktionen und nicht für andere personen erforderlich zu machen. In der Konvention enthält die Dokumentation optionale Parameter wie die id Wahrscheinlichkeit, dass sie in der richtigen Verwendung angezeigt werden.

Für die meisten Apps sollte eine grundlegendes und beschreibendes Routingschema ausgewählt werden, um lesbare und aussagekräftige URLs zu erhalten. Für die konventionelle Standardroute {controller=Home}/{action=Index}/{id?} gilt:

  • Sie unterstützt ein grundlegendes und beschreibendes Routingschema.
  • Sie stellt einen nützlichen Startpunkt für benutzeroberflächenbasierte Apps dar.
  • Ist die einzige Routenvorlage, die für viele Web-UI-Apps benötigt wird. Für größere Web-UI-Apps ist häufig eine andere Route mit Bereichen erforderlich.

MapControllerRoute und MapAreaRoute :

  • Weisen Sie ihren Endpunkten automatisch einen Bestellwert basierend auf der aufgerufenen Reihenfolge zu.

Endpunktrouting in ASP.NET Core 3.0 und höher:

  • Weist kein Konzept für Routen auf.
  • Stellt keine Bestellgarantien für die Ausführung der Erweiterbarkeit bereit, werden alle Endpunkte gleichzeitig verarbeitet.

Wenn Sie die Protokollierung aktivieren, erfahren Sie, wie die integrierten Routingimplementierungen (z.B. Route) Zuordnungen für Anforderungen ermitteln.

Das Attributrouting wird später in diesem Dokument erläutert.

Mehrere herkömmliche Routen

Mehrere herkömmliche Routen können innerhalb UseEndpoints hinzugefügt werden, indem sie weitere Anrufe hinzufügen MapControllerRoute und MapAreaControllerRoute. Dadurch können mehrere Konventionen definiert werden oder herkömmliche Routen hinzugefügt werden, die einer bestimmten Aktion zugeordnet sind, z. B.:

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

Die blog Route im vorherigen Code ist eine dedizierte herkömmliche Route. Es wird als dedizierte herkömmliche Route bezeichnet, da:

Da controller und action nicht in der Routenvorlage "blog/{*article}" als Parameter angezeigt werden:

  • Sie können nur über die Standardwerte { controller = "Blog", action = "Article" }verfügen.
  • Diese Route entspricht immer der Aktion BlogController.Article.

/Blog, /Blog/Articleund /Blog/{any-string} sind die einzigen URL-Pfade, die mit der Blogroute übereinstimmen.

Für das vorherige Beispiel gilt Folgendes:

  • blog route hat eine höhere Priorität für Übereinstimmungen als die default Route, da sie zuerst hinzugefügt wird.
  • Ist ein Beispiel für das Slug-Formatrouting, bei dem es typisch ist, einen Artikelnamen als Teil der URL zu haben.

Warnung

In ASP.NET Core 3.0 und höher wird das Routing nicht ausgeführt:

  • Definieren Sie ein Konzept, das als Route bezeichnet wird. UseRouting fügt der Middlewarepipeline einen Routenabgleich hinzu. In UseRouting der Middleware werden die in der App definierten Endpunkte untersucht und die beste Endpunkt-Übereinstimmung basierend auf der Anforderung ausgewählt.
  • Stellen Sie Garantien für die Ausführungsreihenfolge der Erweiterbarkeit wie IRouteConstraint oder IActionConstraint.

Siehe Routing für Referenzmaterial zum Routing.

Herkömmlicher Routingauftrag

Herkömmliches Routing entspricht nur einer Kombination aus Aktion und Controller, die von der App definiert werden. Dies soll die Fälle vereinfachen, in denen herkömmliche Routen überlappen. Hinzufügen von Routen mithilfe MapControllerRoutevon , MapDefaultControllerRouteund MapAreaControllerRoute weisen Sie ihren Endpunkten automatisch einen Bestellwert basierend auf der aufgerufenen Reihenfolge zu. Übereinstimmungen aus einer Route, die früher angezeigt wird, haben eine höhere Priorität. Beim herkömmlichen Routing ist die Reihenfolge wichtig. Im Allgemeinen sollten Routen mit Gebieten früher platziert werden, da sie spezifischer als Routen ohne Einen Bereich sind. Dedizierte herkömmliche Routen mit Fangen-all-Routenparametern können eine {*article}Route zu gierig machen, d. h. sie entspricht URLs, die Sie von anderen Routen übereinstimmen möchten. Legen Sie die gierigen Routen später in der Routetabelle fest, um gierige Übereinstimmungen zu verhindern.

Warnung

Ein catch-all-Parameter kann aufgrund eines Fehlers beim Routing nicht ordnungsgemäß mit Routen übereinstimmen. Apps, die von diesem Fehler betroffen sind, weisen die folgenden Merkmale auf:

  • Eine catch-all-Route, zum Beispiel {**slug}"
  • Die catch-all-Route kann nicht mit Anforderungen abgeglichen werden, die abgeglichen werden sollen.
  • Durch das Entfernen anderer Routen funktioniert die catch-all-Route.

Weitere Beispiele zu diesem Fehler finden Sie in den GitHub-Issues 18677 und 16579.

Eine Opt-in-Behebung für diesen Fehler ist im .NET Core 3.1.301 SDK und höher enthalten. Der folgende Code legt einen internen Switch fest, mit dem dieser Fehler behoben wird:

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

Auflösen von mehrdeutigen Aktionen

Wenn zwei Endpunkte durch Routing übereinstimmen, muss das Routing eine der folgenden Aktionen ausführen:

  • Wählen Sie den besten Kandidaten aus.
  • Löst eine Ausnahme aus.

Beispiel:

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

Der vorherige Controller definiert zwei Aktionen, die übereinstimmen:

  • Der URL-Pfad /Products33/Edit/17
  • Weiterleiten von Daten { controller = Products33, action = Edit, id = 17 }.

Dies ist ein typisches Muster für MVC-Controller:

  • Edit(int) zeigt ein Formular an, um ein Produkt zu bearbeiten.
  • Edit(int, Product) verarbeitet das gepostete Formular.

So beheben Sie die richtige Route:

  • Edit(int, Product) wird ausgewählt, wenn die Anforderung ein HTTP POSTist.
  • Edit(int) wird ausgewählt, wenn das HTTP-Verb etwas anderes ist. Edit(int) wird im Allgemeinen via GETaufgerufen.

Das HttpPostAttribute, [HttpPost]wird zum Routing bereitgestellt, damit er basierend auf der HTTP-Methode der Anforderung auswählen kann. Dies HttpPostAttribute macht Edit(int, Product) eine bessere Übereinstimmung als Edit(int).

Es ist wichtig, die Rolle von Attributen wie HttpPostAttribute. Ähnliche Attribute werden für andere HTTP-Verben definiert. In herkömmlichem Routing ist es üblich, dass Aktionen denselben Aktionsnamen verwenden, wenn sie Teil eines Showformulars sind, Formularworkflow übermitteln. Weitere Informationen finden Sie unter "Überprüfen der beiden Bearbeitungsaktionsmethoden".

Wenn Routing keinen besten Kandidaten auswählen kann, wird ein AmbiguousMatchException Wurf ausgelöst, der die mehreren übereinstimmend Endpunkte auflisten kann.

Herkömmliche Routennamen

Die Zeichenfolgen und "default" in den folgenden Beispielen "blog" sind herkömmliche Routennamen:

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

Die Routennamen geben der Route einen logischen Namen. Die benannte Route kann für die URL-Generation verwendet werden. Die Verwendung einer benannten Route vereinfacht die URL-Erstellung, wenn die Reihenfolge von Routen die URL-Generation kompliziert machen könnte. Routennamen müssen eine eindeutige Anwendung breit sein.

Routennamen:

  • Haben keine Auswirkungen auf die URL-Übereinstimmung oder die Behandlung von Anforderungen.
  • Wird nur für die URL-Generation verwendet.

Das Routennamenkonzept wird in Routing als IEndpointNameMetadata dargestellt. Name und Endpunktname der Begriffe:

  • Sind austauschbar.
  • Welche in Dokumentation und Code verwendet wird, hängt davon ab, welche API beschrieben wird.

Attributrouting für REST-APIs

REST-APIs sollten Attributrouting verwenden, um die Funktionalität der App als Gruppe von Ressourcen zu modellieren, in denen Vorgänge durch HTTP-Verben dargestellt werden.

Beim Attributrouting werden Aktionen mithilfe von Attributen direkt Routenvorlagen zugeordnet. Der folgende StartUp.Configure Code ist typisch für eine REST-API und wird im nächsten Beispiel verwendet:

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

Im vorhergehenden Code wird innerhalb UseEndpoints aufgerufen, MapControllers um Attributroutencontroller zuzuordnen.

Im folgenden Beispiel:

  • HomeController entspricht einer Reihe von URLs ähnlich wie der Standardroute {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);
    }
}

Die HomeController.Index Aktion wird für eine der URL-Pfade /, , /Home, /Home/Indexoder /Home/Index/3.

In diesem Beispiel wird ein wichtiger Programmierunterschied zwischen Attributrouting und herkömmlichem Routing hervorgehoben. Attributrouting erfordert mehr Eingabe, um eine Route anzugeben. Die herkömmliche Standardroute behandelt Routen genauer. Das Attributrouting ermöglicht jedoch die genaue Kontrolle, welche Routenvorlagen für jede Aktion gelten.

Bei Attributrouting spielen die Controller- und Aktionsnamen keine Rolle, in der eine Aktion übereinstimmt, es sei denn, die Token-Ersatz wird verwendet. Das folgende Beispiel entspricht den gleichen URLs wie im vorherigen Beispiel:

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

Der folgende Code verwendet Token-Ersatz für action und 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();
    }
}

Der folgende Code gilt [Route("[controller]/[action]")] für den 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();
    }
}

Im vorherigen Code müssen die Index Methodenvorlagen vorab oder ~/ an die Routenvorlagen weitergeleitet werden/. Routenvorlagen, die auf eine Aktion angewendet werden, die mit einem / oder ~/ beginnen, können nicht mit Routenvorlagen kombiniert werden, die auf den Controller angewendet werden.

Weitere Informationen zur Auswahl der Routenvorlage finden Sie unter "Route"-Vorlage .

Reservierte Routingnamen

Die folgenden Schlüsselwörter sind reservierte Parameternamen bei Verwendung von Controllern oder Razor Seiten:

  • action
  • area
  • controller
  • handler
  • page

Die Verwendung als Routenparameter mit Attributrouting ist ein gängiger page Fehler. Dies führt zu inkonsistenten und verwirrenden Verhalten bei der URL-Generation.

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

Die speziellen Parameternamen werden von der URL-Generation verwendet, um festzustellen, ob sich ein URL-Generationsvorgang auf eine Razor Seite oder einen Controller bezieht.

  • Die folgenden Schlüsselwörter sind im Kontext einer Razor-Ansicht oder einer Razor-Seite reserviert:
    • page
    • using
    • namespace
    • inject
    • section
    • inherits
    • model
    • addTagHelper
    • removeTagHelper

Diese Schlüsselwörter sollten nicht für Linkgenerationen, modellgebundene Parameter oder Eigenschaften auf oberster Ebene verwendet werden.

HTTP-Verbvorlagen

ASP.NET Core verfügt über die folgenden HTTP-Verbvorlagen:

Routenvorlagen

ASP.NET Core verfügt über die folgenden Routenvorlagen:

Attributrouting mit Http-Verbattributen

Berücksichtigen Sie den folgenden 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);
    }
}

Für den Code oben gilt:

  • Jede Aktion enthält das [HttpGet] Attribut, das nur die Übereinstimmung mit HTTP GET-Anforderungen beschränkt.
  • Die GetProduct Aktion enthält die "{id}" Vorlage, daher id wird der "api/[controller]" Vorlage auf dem Controller angefügt. Die Methodenvorlage lautet "api/[controller]/"{id}"". Daher entspricht diese Aktion nur GET-Anforderungen für das Formular/api/test2/xyz,/api/test2/{any string}/api/test2/123 usw.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • Die GetIntProduct Aktion enthält die "int/{id:int}") Vorlage. Der :int Teil der Vorlage beschränkt die Routenwerte auf Zeichenfolgen, die id in eine ganze Zahl konvertiert werden können. Eine GET-Anforderung an /api/test2/int/abc:
    • Entspricht dieser Aktion nicht.
    • Gibt einen Fehler "404 Nicht gefunden" zurück.
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • Die GetInt2Product Aktion enthält {id} in der Vorlage, beschränkt sich jedoch nicht id auf Werte, die in eine ganze Zahl konvertiert werden können. Eine GET-Anforderung an /api/test2/int2/abc:
    • Entspricht dieser Route.
    • Die Modellbindung kann nicht in eine ganze Zahl konvertiert abc werden. Der id Parameter der Methode ist ganze Zahl.
    • Gibt eine 400 schlechte Anforderung zurück, da die Modellbindung nicht in eine ganze Zahl konvertiertabc werden konnte.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

Attributrouting kann Attribute wie , HttpPostAttributeHttpPutAttributeuswHttpDeleteAttribute. verwendenHttpMethodAttribute. Alle HTTP-Verbattribute akzeptieren eine Routevorlage. Im folgenden Beispiel werden zwei Aktionen gezeigt, die mit derselben Routenvorlage übereinstimmen:

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

Verwenden des URL-Pfads /products3:

  • Die MyProductsController.ListProducts Aktion wird ausgeführt, wenn das HTTP-Verb lautet GET.
  • Die MyProductsController.CreateProduct Aktion wird ausgeführt, wenn das HTTP-Verb lautet POST.

Beim Erstellen einer REST-API ist es selten, dass Sie für eine Aktionsmethode verwenden [Route(...)] müssen, da die Aktion alle HTTP-Methoden akzeptiert. Es ist besser, das spezifischere HTTP-Verb-Attribut zu verwenden, um genau zu sein, was Ihre API unterstützt. REST-API-Clients sollten wissen, welche Pfade und HTTP-Verben bestimmten logischen Operationen entsprechen.

REST-APIs sollten Attributrouting verwenden, um die Funktionalität der App als Gruppe von Ressourcen zu modellieren, in denen Vorgänge durch HTTP-Verben dargestellt werden. Dies bedeutet, dass viele Vorgänge, z. B. GET und POST auf derselben logischen Ressource, dieselbe URL verwenden. Das Attributrouting bietet eine Ebene der Steuerung, die für einen sorgfältigen Entwurf des öffentlichen Endpunktlayouts einer API erforderlich ist.

Da eine Attributroute für eine bestimmte Aktion gilt, ist es einfach, Parameter als Teil der Routenvorlagendefinition erforderlich festzulegen. Im folgenden Beispiel id ist als Teil des URL-Pfads erforderlich:

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

Die Products2ApiController.GetProduct(int) Aktion:

  • Wird mit URL-Pfad ausgeführt wie z. B. /products2/3
  • Wird nicht mit dem URL-Pfad /products2ausgeführt.

Das [Consumes]-Attribut ermöglicht es einer Aktion, die unterstützten Anforderungsinhaltstypen einzuschränken. Weitere Informationen finden Sie unter Definieren unterstützter Anforderungsinhaltstypen mit dem Attribut "Consumes".

Eine vollständige Beschreibung und Routenvorlagen und dazugehörige Optionen finden Sie unter Routing in ASP.NET Core.

Weitere Informationen finden Sie unter [ApiController]ApiController-Attribut.

Routenname

Der folgende Code definiert einen Routennamen von Products_List:

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

Routennamen können verwendet werden, um basierend auf einer bestimmten Route eine URL zu generieren. Routennamen:

  • Haben keine Auswirkungen auf das URL-Übereinstimmungsverhalten des Routings.
  • Wird nur für die URL-Generation verwendet.

Routennamen müssen anwendungsweit eindeutig sein.

Kontrastiert den vorherigen Code mit der herkömmlichen Standardroute, die den id Parameter als optional ({id?}) definiert. Die Möglichkeit, APIs genau anzugeben, hat Vorteile, z. B. das Zulassen /products und /products/5 Senden an unterschiedliche Aktionen.

Kombinieren von Attributrouten

Um Attributrouting weniger repetitiv zu gestalten, werden Routenattribute auf dem Controller mit Routenattributen auf den einzelnen Aktionen kombiniert. Alle auf dem Controller definierten Routenvorlagen werden den Routenvorlagen auf den Aktionen vorangestellt. Wenn Routenattribute auf dem Controller platziert werden, verwenden alle Aktionen im Controller Attributrouting.

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

Im vorherigen Beispiel:

  • Der URL-Pfad /products kann übereinstimmen. ProductsApi.ListProducts
  • Der URL-Pfad /products/5 kann übereinstimmen ProductsApi.GetProduct(int).

Beide Aktionen entsprechen nur HTTP GET , da sie mit dem [HttpGet] Attribut gekennzeichnet sind.

Routenvorlagen, die auf eine Aktion angewendet werden, die mit einem / oder ~/ beginnen, können nicht mit Routenvorlagen kombiniert werden, die auf den Controller angewendet werden. Das folgende Beispiel entspricht einem Satz von URL-Pfaden ähnlich der Standardroute.

[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 der folgenden Tabelle werden die [Route] Attribute im vorherigen Code erläutert:

Attribut Kombiniert mit [Route("Home")] Definiert die Route-Vorlage
[Route("")] Ja "Home"
[Route("Index")] Ja "Home/Index"
[Route("/")] Nein ""
[Route("About")] Ja "Home/About"

Attributroutenreihenfolge

Routing erstellt eine Struktur und entspricht allen Endpunkten gleichzeitig:

  • Die Routeneinträge verhalten sich wie bei einer idealen Reihenfolge.
  • Die spezifischsten Routen haben eine Chance, vor den allgemeineren Routen auszuführen.

Beispielsweise ist eine Attributroute wie eine Attributroute spezifischer als eine Attributroute wie blog/search/{topic}blog/{*article}. Die blog/search/{topic} Route hat standardmäßig höhere Priorität, da es spezifischer ist. Mit herkömmlichem Routing ist der Entwickler für das Platzieren von Routen in der gewünschten Reihenfolge verantwortlich.

Attributrouten können eine Bestellung mithilfe der Order Eigenschaft konfigurieren. Alle bereitgestellten Routingattribute des Frameworks umfassen Order . Routen werden entsprechend einer aufsteigenden Reihenfolge der Order-Eigenschaft verarbeitet. Die Standardreihenfolge ist 0. Festlegen einer Route mithilfe Order = -1 von Ausführungen vor Routen, die keine Bestellung festlegen. Festlegen einer Route mithilfe der Ausführung nach Order = 1 der Standardroutenreihenfolge.

Vermeiden Sie je nach Order. Wenn der URL-Bereich einer App explizite Bestellwerte erfordert, die ordnungsgemäß weitergeleitet werden sollen, ist es wahrscheinlich auch für Clients verwirrend. Im Allgemeinen wählt das Attributrouting die richtige Route mit URL-Übereinstimmung aus. Wenn die Standardreihenfolge, die für die URL-Generation verwendet wird, nicht funktioniert, ist die Verwendung eines Routennamens als Außerkraftsetzung in der Regel einfacher als das Anwenden der Order Eigenschaft.

Berücksichtigen Sie die folgenden beiden Controller, die beide die Routenabgleiche /homedefinieren:

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

Die Anforderung /home mit dem vorherigen Code löst eine Ausnahme ähnlich wie folgt aus:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

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

Das Hinzufügen Order zu einem der Routenattribute löst die Unklarheit aus:

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

Führen Sie mit dem vorherigen Code /home den HomeController.Index Endpunkt aus. Um zum MyDemoController.MyIndex, anfordern /home/MyIndex. Hinweis:

  • Der vorherige Code ist ein Beispiel oder ein schlechtes Routingdesign. Es wurde verwendet, um die Order Eigenschaft zu veranschaulichen.
  • Die Order Eigenschaft löst nur die Unklarheit auf, diese Vorlage kann nicht übereinstimmen. Es wäre besser, die [Route("Home")] Vorlage zu entfernen.

Siehe Razor Seitenrouten- und App-Konventionen: Route order for information on route order with Razor Pages.

In einigen Fällen wird ein HTTP 500-Fehler mit mehrdeutigen Routen zurückgegeben. Verwenden Sie die Protokollierung , um zu sehen, welche Endpunkte dies AmbiguousMatchExceptionverursacht haben.

Token-Ersatz in Routenvorlagen [Controller], [Aktion], [Bereich]

Die Attributrouten unterstützen die Tokenbesetzung durch schließendes Token in quadratische Klammern ([, ]). Die Token [action], [area]und [controller] werden durch die Werte des Aktionsnamens, des Bereichsnamens und des Controllernamens aus der Aktion ersetzt, in der die Route definiert ist:

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

Für den Code oben gilt:

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

Die Tokenersetzung tritt im letzten Schritt der Erstellung von Attributrouten auf. Im vorhergehenden Beispiel verhält sich dasselbe wie der folgende 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);
    }
}

Wenn Sie dies in einer anderen Sprache als Englisch lesen, informieren Sie uns in diesem GitHub Diskussionsproblem, wenn Sie die Codekommentare in Ihrer Muttersprache sehen möchten.

Attributrouten können auch mit Vererbung kombiniert werden. Dies ist leistungsfähig, kombiniert mit Token-Ersatz. Tokenersetzung gilt auch für Routennamen, die durch Attributrouten definiert werden. [Route("[controller]/[action]", Name="[controller]_[action]")]generiert einen eindeutigen Routennamen für jede Aktion:

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

Um mit dem Literaltokentrennzeichen [ übereinzugleichen oder ]es zu escape, indem Sie das Zeichen ([[ oder ]]) wiederholen.

Verwenden eines Parametertransformators zum Anpassen der Tokenersetzung

Die Tokenersetzung kann mit einem Parametertransformator angepasst werden. Ein Parametertransformator implementiert IOutboundParameterTransformer und wandelt den Wert der Parameter um. Beispielsweise ändert ein benutzerdefinierter SlugifyParameterTransformer Parametertransformor den SubscriptionManagement Routenwert 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();
    }
}

Die RouteTokenTransformerConvention ist eine Anwendungsmodellkonvention, die Folgendes ausführt:

  • Wendet einen angegebenen Parametertransformator auf alle Attributrouten in der App an.
  • Passt die Tokenwerte für Attributrouten bei ihrer Ersetzung an.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Die vorhergehende ListAll Methode entspricht /subscription-management/list-all.

Die RouteTokenTransformerConvention wird als Option in ConfigureServices registriert.

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

Weitere Informationen finden Sie unter MDN-Webdokumenten in Slug für die Definition von Slug.

Warnung

Übergeben Sie ein Timeout, wenn Sie System.Text.RegularExpressions zum Verarbeiten nicht vertrauenswürdiger Eingaben verwenden. Ein böswilliger Benutzer kann Eingaben für RegularExpressions angeben, um einen Denial-of-Service-Angriff durchzuführen. ASP.NET Core-Framework-APIs, die RegularExpressions verwenden, übergeben ein Timeout.

Mehrere Attributrouten

Attributrouting unterstützt das Definieren mehrerer Routen, die zu derselben Aktion führen. Dies wird am häufigsten beim Imitieren des Verhaltens der herkömmlichen Standardroute verwendet. Im folgenden Beispiel wird dies gezeigt:

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

Das Platzieren mehrerer Routenattribute auf dem Controller bedeutet, dass jeder mit jedem der Routenattribute auf den Aktionsmethoden kombiniert:

[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-Verbrouteneinschränkungen implementieren .IActionConstraint

Wenn mehrere Routingattribute, die implementiert IActionConstraint werden, in einer Aktion platziert werden:

  • Jede Aktionseinschränkung kombiniert die Routevorlage, die auf den Controller angewendet wird.
[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();
    }
}

Die Verwendung mehrerer Routen für Aktionen scheint nützlich und leistungsfähig zu sein, es ist besser, den URL-Speicherplatz Ihrer App einfach und gut definiert zu halten. Verwenden Sie mehrere Routen nur bei Bedarf, um vorhandene Clients zu unterstützen.

Angeben von optionalen Attributroutenparametern, Standardwerten und Einschränkungen

Attributrouten unterstützen dieselbe Inline-Syntax wie herkömmliche Routen, um optionale Parameter, Standardwerte und Einschränkungen anzugeben.

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

Wendet im vorherigen Code eine Routeeinschränkung [HttpPost("product14/{id:int}")] an. Die Products14Controller.ShowProduct Aktion wird nur mit URL-Pfaden wie /product14/3. Der Abschnitt {id:int} "Route"-Vorlage beschränkt das Segment nur auf ganze Zahlen.

Eine ausführliche Beschreibung der Syntax der Routenvorlage finden Sie unter Routenvorlagenreferenz.

Benutzerdefinierte Routenattribute mithilfe von IRouteTemplateProvider

Alle Routenattribute implementieren IRouteTemplateProvider. Die ASP.NET Core Laufzeit:

  • Sucht nach Attributen für Controllerklassen und Aktionsmethoden, wenn die App gestartet wird.
  • Verwendet die Attribute, die implementiert werden, IRouteTemplateProvider um die anfängliche Gruppe von Routen zu erstellen.

Implementieren Sie IRouteTemplateProvider , um benutzerdefinierte Routenattribute zu definieren. Jeder IRouteTemplateProvider lässt Sie eine einzelne Route mit einer benutzerdefinierten Routenvorlage, Reihenfolge und einem benutzerdefinierten Namen definieren:

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

Die vorherige Get Methode gibt Order = 2, Template = api/MyTestApizurück .

Verwenden des Anwendungsmodells zum Anpassen von Attributrouten

Das Anwendungsmodell:

  • Ist ein Objektmodell, das beim Start erstellt wurde.
  • Enthält alle Metadaten, die von ASP.NET Core zum Weiterleiten und Ausführen der Aktionen in einer App verwendet werden.

Das Anwendungsmodell enthält alle Daten, die aus Routenattributen gesammelt werden. Die Daten aus Routenattributen werden von der IRouteTemplateProvider Implementierung bereitgestellt. Konventionen:

  • Kann geschrieben werden, um das Anwendungsmodell zu ändern, um das Verhalten des Routings anzupassen.
  • Werden beim Start der App gelesen.

In diesem Abschnitt wird ein grundlegendes Beispiel zum Anpassen des Routings mithilfe des Anwendungsmodells dargestellt. Der folgende Code macht Routen ungefähr mit der Ordnerstruktur des Projekts zusammen.

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

Der folgende Code verhindert, dass die Konvention auf Controller angewendet wird, die namespace Attribut weitergeleitet werden:

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

Beispielsweise wird der folgende Controller nicht verwendet NamespaceRoutingConvention:

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

Die NamespaceRoutingConvention.Apply-Methode:

  • Macht nichts, wenn der Controller attributiert wird.
  • Legt die Controllervorlage basierend auf dem namespace, wobei die Basis namespace entfernt wurde.

Dies NamespaceRoutingConvention kann 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.

Berücksichtigen Sie beispielsweise den folgenden 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}");
        }
    }
}

Für den Code oben gilt:

  • Die Basis namespace ist My.Application.
  • Der vollständige Name des vorherigen Controllers ist My.Application.Admin.Controllers.UsersController.
  • Die NamespaceRoutingConvention Controllervorlage wird auf Admin/Controllers/Users/[action]/{id?.

Dies NamespaceRoutingConvention kann auch als Attribut auf einem Controller angewendet werden:

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

Gemischtes Routing: Attributrouting vs. herkömmliches Routing

ASP.NET Core Apps können die Verwendung herkömmlicher Routing- und Attributroutings kombinieren. In der Regel werden herkömmliche Routen für Controller verwendet, die für Browser-HTML-Seiten gedacht sind, und das Attributrouting für Controller für REST-APIs.

Aktionen werden entweder herkömmlich oder über Attribute zugeordnet, d.h., dass eine Route auf dem Controller oder der Aktion platziert wird. Aktionen, die Attributrouten definieren, können nicht über die herkömmliche Routen und umgekehrt erreicht werden. Jedes Route-Attribut auf dem Controller führt alle Aktionen im Controller-Attribut weiter.

Attributrouting und herkömmliches Routing verwenden dasselbe Routingmodul.

URL-Generation und Umgebungswerte

Apps können Routing-URL-Generierungsfeatures verwenden, um URL-Links zu Aktionen zu generieren. Durch das Generieren von URLs werden Hardcodierungs-URLs beseitigt, wodurch Code robuster und wartungsfähiger wird. Dieser Abschnitt konzentriert sich auf die von MVC bereitgestellten URL-Generationsfeatures und deckt nur Grundlagen der Funktionsweise der URL-Generation ab. Eine detaillierte Beschreibung der URL-Generierung finden Sie unter Routing in ASP.NET Core.

Die IUrlHelper Schnittstelle ist das zugrunde liegende Element der Infrastruktur zwischen MVC und Routing für die URL-Generation. Eine Instanz von IUrlHelper ist über die Url Eigenschaft in Controllern, Ansichten und Ansichtskomponenten verfügbar.

Im folgenden Beispiel wird die IUrlHelper Schnittstelle über die Controller.Url Eigenschaft verwendet, um eine URL zu einer anderen Aktion zu generieren.

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

Wenn die App die herkömmliche Standardroute verwendet, ist der Wert der url Variablen die URL-Pfadzeichenfolge /UrlGeneration/Destination. Dieser URL-Pfad wird durch Routing erstellt, indem Folgendes kombiniert wird:

  • Die Routenwerte aus der aktuellen Anforderung, die als Umgebungswerte bezeichnet werden.
  • Die an diese Werte übergebenen Url.Action und substituieren diese Werte in die Routenvorlage:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

Der Wert eines jeden Routenparameters wird in der Routenvorlage durch die entsprechenden Namen mit den Werten und Umgebungswerten ersetzt. Ein Routenparameter, der keinen Wert hat, kann:

  • Verwenden Sie einen Standardwert, wenn er einen hat.
  • Werden Sie übersprungen, wenn es optional ist. Beispiel: Die id aus der Routenvorlage {controller}/{action}/{id?}.

Fehler bei der URL-Generierung, wenn ein erforderlicher Routenparameter keinen entsprechenden Wert hat. Wenn die URL-Generierung für eine Route fehlschlägt, wird die nächste Route ausprobiert, bis alle Routen getestet wurden oder eine Übereinstimmung gefunden wurde.

Im vorherigen Beispiel wird Url.Action davon ausgegangen , dass herkömmliches Routing ausgeführt wird. Die URL-Generation funktioniert ähnlich mit dem Attributrouting, obwohl die Konzepte unterschiedlich sind. Mit herkömmlichem Routing:

  • Die Routenwerte werden verwendet, um eine Vorlage zu erweitern.
  • Die Routenwerte für controller diese Vorlage und action werden normalerweise in dieser Vorlage angezeigt. Dies funktioniert, da die URLs, die durch Routing übereinstimmen, einer Konvention entsprechen.

Im folgenden Beispiel wird attributweiterleitung verwendet:

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

Die Source Aktion im vorherigen Code generiert custom/url/to/destination.

LinkGeneratorwurde in ASP.NET Core 3.0 als Alternative zu IUrlHelper. LinkGenerator bietet ähnliche, aber flexiblere Funktionen. Jede Methode enthält IUrlHelper auch eine entsprechende Familie von Methoden LinkGenerator .

Generieren von URLs nach Aktionsnamen

Url.Action, LinkGenerator.GetPathByAction und alle zugehörigen Überladungen sollen den Zielendpunkt generieren, indem ein Controllername und Aktionsname angegeben wird.

Bei Verwendung Url.Actionwerden die aktuellen Routenwerte für controller die action Laufzeit bereitgestellt:

  • Der Wert und controlleraction sind Teil von Umgebungswerten und Werten. Die Methode Url.Action verwendet immer die aktuellen Werte action und controller generiert einen URL-Pfad, der an die aktuelle Aktion weitergeleitet wird.

Routing versucht, die Werte in Umgebungswerten zu verwenden, um Informationen auszufüllen, die beim Generieren einer URL nicht bereitgestellt wurden. Betrachten Sie eine Route wie {a}/{b}/{c}/{d} bei Umgebungswerten { a = Alice, b = Bob, c = Carol, d = David }:

  • Das Routing verfügt über genügend Informationen, um eine URL ohne zusätzliche Werte zu generieren.
  • Routing verfügt über genügend Informationen, da alle Routenparameter einen Wert haben.

Wenn der Wert { d = Donovan } hinzugefügt wird:

  • Der Wert { d = David } wird ignoriert.
  • Der generierte URL-Pfad ist Alice/Bob/Carol/Donovan.

Warnung: URL-Pfade sind hierarchisch. Wenn der Wert { c = Cheryl } im vorherigen Beispiel hinzugefügt wird:

  • Beide Werte { c = Carol, d = David } werden ignoriert.
  • Es gibt keinen Wert mehr, d und die URL-Generation schlägt fehl.
  • Die gewünschten Werte c und d müssen angegeben werden, um eine URL zu generieren.

Möglicherweise erwarten Sie, dass dieses Problem mit der Standardroute erreicht wird {controller}/{action}/{id?}. Dieses Problem ist in der Praxis selten, da Url.Action immer explizit ein und action ein controller Wert angegeben wird.

Mehrere Überladungen von Url.Action führen ein Routingwertobjekt aus, um Werte für andere Routenparameter als controller und action. Das Route values-Objekt wird häufig mit id. Beispiel: Url.Action("Buy", "Products", new { id = 17 }). Das Route values-Objekt:

  • In der Regel handelt es sich um ein Objekt anonymer Art.
  • Kann ein IDictionary<> oder ein POCO sein).

Alle zusätzlichen Routenwerte, die keinen Routenparametern zugeordnet sind, werden in der Abfragezeichenfolge platziert.

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

Der vorherige Code generiert /Products/Buy/17?color=red.

Der folgende Code generiert eine absolute URL:

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

Verwenden Sie eine der folgenden Optionen, um eine absolute URL zu erstellen:

  • Eine Überladung, die eine protocol. Beispiel: der vorherige Code.
  • LinkGenerator.GetUriByAction, das standardmäßig absolute URIs generiert.

Generieren von URLs nach Route

Der vorherige Code veranschaulichte das Generieren einer URL durch Übergeben des Controllers und des Aktionsnamens. IUrlHelper stellt auch die Url.RouteUrl-Familie von Methoden bereit. Diese Methoden ähneln Url.Action, aber sie kopieren nicht die aktuellen Werte action und controller in die Routenwerte. Die häufigste Verwendung von Url.RouteUrl:

  • Gibt einen Routennamen an, der die URL generiert.
  • In der Regel wird kein Controller- oder Aktionsname angegeben.
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();
    }

Die folgende Razor Datei generiert einen HTML-Link zum Destination_Route:

<h1>Test Links</h1>

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

Generieren von URLs in HTML und Razor

IHtmlHelperstellt die HtmlHelper Methoden Html.BeginForm und Html.ActionLink bereit, um elemente zu generieren und <a> zu generieren<form>. Diese Methoden verwenden die Url.Action-Methode , um eine URL zu generieren und ähnliche Argumente anzunehmen. Die Url.RouteUrl-Begleiter für HtmlHelper sind Html.BeginRouteForm und Html.RouteLink, die ähnliche Funktionen aufweisen.

Taghilfsprogramme generieren URLs mit den Taghilfsprogrammen form und <a>. Beide implementieren mit IUrlHelper. Weitere Informationen finden Sie unter Taghilfen in Formularen .

In Ansichten ist IUrlHelper über die Url-Eigenschaft für jede Ad-hoc-URL-Generierung verfügbar, die keine der oben genannten ist.

URL-Generation in Aktionsergebnissen

Die vorherigen Beispiele zeigen die Verwendung IUrlHelper in einem Controller. Die häufigste Verwendung in einem Controller besteht darin, eine URL als Teil eines Aktionsergebnisses zu generieren.

Die Basisklassen ControllerBase und Controller stellen Hilfsmethoden für Aktionsergebnisse bereit, die auf eine andere Aktionen verweisen. Eine typische Verwendung besteht darin, nach der Annahme der Benutzereingabe umzuleiten:

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

Die Aktionsergebnisse-Factorymethoden wie z RedirectToAction . B. und CreatedAtAction folgen einem ähnlichen Muster wie den Methoden für IUrlHelper.

Sonderfall für dedizierte herkömmliche Routen

Herkömmliches Routing kann eine spezielle Art von Routendefinition verwenden, die als dedizierte konventionelle Route bezeichnet wird. Im folgenden Beispiel ist die benannte blog Route eine dedizierte konventionelle 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?}");
});

Generiert mithilfe der vorherigen Routendefinitionen Url.Action("Index", "Home") den URL-Pfad / mithilfe der default Route, aber warum? Man könnte meinen, dass die Routenwerte { controller = Home, action = Index } ausreichen würden, um eine URL mithilfe von blog zu generieren, und das Ergebnis wäre /blog?action=Index&controller=Home.

Dedizierte konventionelle Routen basieren auf einem speziellen Verhalten von Standardwerten, die keinen entsprechenden Routenparameter haben, der verhindert, dass die Route mit der URL-Generation zu gierig ist. In diesem Fall sind die Standardwerte { controller = Blog, action = Article }, und weder controller noch action werden als Routenparameter verwendet. Wenn das Routing die URL-Generierung ausführt, müssen die angegebenen Werte mit den Standardwerten übereinstimmen. Fehler bei der URL-Generation, blog da die Werte { controller = Home, action = Index } nicht übereinstimmen { controller = Blog, action = Article }. Routing greift dann wieder auf default zurück, was erfolgreich ausgeführt wird.

Bereiche

Bereiche sind ein MVC-Feature, das zum Organisieren verwandter Funktionen in eine Gruppe als separates verwendet wird:

  • Routing-Namespace für Controlleraktionen.
  • Ordnerstruktur für Ansichten.

Die Verwendung von Bereichen ermöglicht es einer App, mehrere Controller mit demselben Namen zu haben, solange sie unterschiedliche Bereiche haben. Mithilfe von Bereichen wird außerdem eine Hierarchie erstellt, damit das Routing durch Hinzufügen eines anderen Routenparameters ausgeführt werden kann: area zu controller und action. In diesem Abschnitt wird erläutert, wie das Routing mit Bereichen interagiert. Weitere Informationen dazu, wie Bereiche mit Ansichten verwendet werden.

Im folgenden Beispiel wird MVC so konfiguriert, dass die standardmäßige herkömmliche Route und eine Route für einen areaarea benannten Namen Blogverwendet wird:

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

Im vorherigen Code MapAreaControllerRoute wird zum Erstellen der "blog_route". Der zweite Parameter ist "Blog"der Bereichsname.

Wenn sie einen URL-Pfad wie /Manage/Users/AddUserfolgt übereinstimmen, generiert die "blog_route" Route die Routenwerte { area = Blog, controller = Users, action = AddUser }. Der area Routewert wird von einem Standardwert für area. Die von MapAreaControllerRoute ihnen erstellte Route entspricht den folgenden Schritten:

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 erstellt mit einem Standardwert und einer Einschränkung für area sowie mit dem bereitgestellten Bereichsnamen (in diesem Fall Blog) eine Route. Der Standardwert stellt sicher, dass die Route immer { area = Blog, ... } erzeugt, die Einschränkung erfordert den Wert { area = Blog, ... } für URL-Generierung.

Beim herkömmlichen Routing ist die Reihenfolge wichtig. Im Allgemeinen sollten Routen mit Gebieten früher platziert werden, da sie spezifischer als Routen ohne Einen Bereich sind.

Mit dem vorherigen Beispiel entsprechen die Routenwerte { area = Blog, controller = Users, action = AddUser } der folgenden Aktion:

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

Das Attribut [Area] ist, was einen Controller als Teil eines Bereichs angibt. Dieser Controller befindet sich im Blog Bereich. Controller ohne Attribut sind keine Mitglieder eines [Area] Bereichs und stimmen nicht überein, wenn der area Routewert durch Routing bereitgestellt wird. Im folgenden Beispiel kann nur der erste aufgelistete Controller die Routenwerte { area = Blog, controller = Users, action = AddUser } abgleichen.

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

Der Namespace jedes Controllers wird hier für Die Vollständigkeit angezeigt. Wenn die vorherigen Controller denselben Namespace verwendet haben, wird ein Compilerfehler generiert. Klassennamespaces haben keine Auswirkungen auf das MVC-Routing.

Die ersten beiden Controller gehören zu Bereichen und werden können nur abgleichen, wenn ihr jeweiliger Bereichsname vom area-Routenwert bereitgestellt wird. Der dritte Controller gehört keinem Bereich an und kann nur abgleichen, wenn vom Routing kein Wert für area bereitgestellt wird.

Im Hinblick auf das Erkennen keines Werts hat die Abwesenheit des area-Werts dieselben Auswirkungen, wie wenn der Wert für area 0 (null) oder eine leere Zeichenfolge wäre.

Beim Ausführen einer Aktion innerhalb eines Bereichs steht der Routewert area als Umgebungswert zur Verwendung für die URL-Generation zur Verfügung. Das bedeutet, dass Bereiche bei der URL-Generierung wie im folgenden Beispiel dargestellt standardmäßig beständig sind.

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

Der folgende Code generiert eine URL zu /Zebra/Users/AddUser:

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

Aktionsdefinition

Öffentliche Methoden auf einem Controller, außer denen mit dem NonAction-Attribut , sind Aktionen.

Beispielcode

Debugdiagnose

Legen Sie für eine ausführliche Routingdiagnoseausgabe Logging:LogLevel:Microsoft auf Debug fest. Legen Sie in der Entwicklungsumgebung die Protokollebene in appsettings.Development.json fest:

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