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:
- Herkömmliches Routing wird in der Regel mit Controllern und Ansichten verwendet.
- Attributrouting , das mit REST-APIs verwendet wird. Wenn Sie in erster Linie für das Routing für REST-APIs interessiert sind, wechseln Sie zum Attributrouting für REST-APIs .
- 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 namensProductsController
und eineDetails
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 Wertid = 5
, um denid
Parameter auf5
festzulegen. Weitere Details finden Sie unter Modellbindung .{controller=Home}
Home
definiert als Standardcontroller
.{action=Index}
Index
definiert als Standardaction
.Das
?
Zeichen in{id?}
definiertid
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 HomeController
Index
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:
- Rufen Sie MapControllers auf, um routingfähige Controller zu zuordnen.
- Aufrufen MapControllerRoute oder MapAreaControllerRoute, um sowohl herkömmliche Routingcontroller als auch Attributroutencontroller zuzuordnen.
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 optionaleid
. 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 dieProductsController.List
Aktion zu./Blog/Article/17
BlogController.Article
bezieht den Parameter in derid
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 auf0
die Modellbindung festgelegt.- Es wird keine Entität im Datenbankabgleich
id == 0
gefunden.
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:
- Es verwendet herkömmliches Routing.
- Es ist für eine bestimmte Aktion gewidmet.
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/Article
und /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 diedefault
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. InUseRouting
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 HTTPPOST
ist.Edit(int)
wird ausgewählt, wenn das HTTP-Verb etwas anderes ist.Edit(int)
wird im Allgemeinen viaGET
aufgerufen.
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/Index
oder /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, daherid
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 dieid
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 aufid
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. Derid
Parameter der Methode ist ganze Zahl. - Gibt eine ungültige Anforderung von 400 zurück, da die Modellbindung nicht in eine ganze Zahl konvertiert werden
abc
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 istGET
. - Die
MyProductsController.CreateProduct
Aktion wird ausgeführt, wenn das HTTP-Verb istPOST
.
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
/products2
ausgefü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 übereinstimmenProductsApi.ListProducts
- Der URL-Pfad
/products/5
kann übereinstimmenProductsApi.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 /home
definieren:
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 AmbiguousMatchException
verursacht 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/MyTestApi
zurück .
Verwenden des Anwendungsmodells zum Anpassen von Attributrouten
Das Anwendungsmodell:
- Ist ein Objektmodell, das beim Start in
Program.cs
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 Basisnamespace
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
istMy.Application
. - Der vollständige Name des vorherigen Controllers ist
My.Application.Admin.Controllers.UsersController
. - Die
NamespaceRoutingConvention
Vorlage "Controller" wird aufAdmin/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 IUrlHelper
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 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 undaction
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.Action
werden die aktuellen Routenwerte für controller
die action
Laufzeit bereitgestellt:
- Der Wert und
controller
action
sind Teil von Umgebungswerten und Werten. Die MethodeUrl.Action
verwendet immer die aktuellen Werteaction
undcontroller
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
undd
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 Blog
Route 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
- MyDisplayRouteInfo wird von dem NuGet-Paket Rick.Docs.Samples.RouteInfo bereitgestellt und zeigt Routeninformationen an.
- Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
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:
- Herkömmliches Routing wird in der Regel mit Controllern und Ansichten verwendet.
- Attributrouting , das mit REST-APIs verwendet wird. Wenn Sie in erster Linie für das Routing für REST-APIs interessiert sind, wechseln Sie zum Attributrouting für REST-APIs .
- 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 namensProductsController
und eineDetails
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 Wertid = 5
, um denid
Parameter auf5
festzulegen. Weitere Details finden Sie unter Modellbindung .{controller=Home}
Home
definiert als Standardcontroller
.{action=Index}
Index
definiert als Standardaction
.Das
?
Zeichen in{id?}
definiertid
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 HomeController
Index
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:
- Rufen Sie MapControllers ein
UseEndpoints
, um attributroutete Controller zuzuordnen. - Aufrufen MapControllerRoute oder MapAreaControllerRoute, um sowohl herkömmliche Routingcontroller als auch Attributroutencontroller zuzuordnen.
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 optionaleid
. 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 dieProductsController.List
Aktion zu./Blog/Article/17
BlogController.Article
bezieht den Parameter in derid
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 auf0
die Modellbindung festgelegt.- Es wird keine Entität im Datenbankabgleich
id == 0
gefunden.
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:
- Es verwendet herkömmliches Routing.
- Es ist für eine bestimmte Aktion gewidmet.
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/Article
und /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 diedefault
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. InUseRouting
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 HTTPPOST
ist.Edit(int)
wird ausgewählt, wenn das HTTP-Verb etwas anderes ist.Edit(int)
wird im Allgemeinen viaGET
aufgerufen.
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/Index
oder /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, daherid
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, dieid
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 nichtid
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. Derid
Parameter der Methode ist ganze Zahl. - Gibt eine 400 schlechte Anforderung zurück, da die Modellbindung nicht in eine ganze Zahl konvertiert
abc
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 lautetGET
. - Die
MyProductsController.CreateProduct
Aktion wird ausgeführt, wenn das HTTP-Verb lautetPOST
.
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
/products2
ausgefü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 übereinstimmenProductsApi.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 /home
definieren:
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 AmbiguousMatchException
verursacht 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/MyTestApi
zurü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 Basisnamespace
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
istMy.Application
. - Der vollständige Name des vorherigen Controllers ist
My.Application.Admin.Controllers.UsersController
. - Die
NamespaceRoutingConvention
Controllervorlage wird aufAdmin/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 undaction
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.Action
werden die aktuellen Routenwerte für controller
die action
Laufzeit bereitgestellt:
- Der Wert und
controller
action
sind Teil von Umgebungswerten und Werten. Die MethodeUrl.Action
verwendet immer die aktuellen Werteaction
undcontroller
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
undd
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 area
area
benannten Namen Blog
verwendet 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/AddUser
folgt ü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
- MyDisplayRouteInfo wird von dem NuGet-Paket Rick.Docs.Samples.RouteInfo bereitgestellt und zeigt Routeninformationen an.
- Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
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"
}
}
}