Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Par Ryan Nowak, Kirk Larkinet Rick Anderson
Note
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 10 de cet article.
Warning
Cette version de ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la politique de support de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 10 de cet article.
Les contrôleurs ASP.NET Core utilisent le middleware de routage pour faire correspondre les URL des requêtes entrantes et les mapper à des actions. Modèles d’itinéraire :
- Sont définis au démarrage dans
Program.csou dans les attributs. - Décrire comment les chemins d’accès d’URL sont mis en correspondance avec les actions.
- Sont utilisés pour générer des URL pour les liens. Les liens générés sont généralement retournés dans les réponses.
Les actions sont routées de façon conventionnelle ou routées par attribut. Le fait d’appliquer une route au contrôleur ou à l’action active le routage par attributs. Pour plus d’informations, consultez Routage mixte.
Ce document :
- Explique les interactions entre MVC et le routage :
- Comment les applications MVC classiques utilisent les fonctionnalités de routage?
- Couvre les deux :
- Routage conventionnel généralement utilisé avec les contrôleurs et les vues.
- Routage d’attributs utilisé avec REST les API. Si vous êtes principalement intéressé par le routage pour REST les API, accédez à la section Routage des attributs pour REST les API.
- Pour plus d’informations sur le routage avancé, consultez Routage.
- Fait référence au système de routage par défaut comme routage de point de terminaison. Vous pouvez utiliser des contrôleurs avec la version précédente du routage à des fins de compatibilité. Pour obtenir des instructions, consultez le guide de migration 2.2-3.0.
Configurer la route conventionnelle
Le modèle MVC ASP.NET Core génère un routage conventionnel similaire à l’exemple suivant :
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();
Utilisez MapControllerRoute pour créer un seul itinéraire. L’itinéraire unique est l’itinéraire default . La plupart des applications qui utilisent des contrôleurs et des vues emploient un modèle de route similaire à la route default.
REST Les API doivent utiliser le routage d’attributs.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Le modèle de route "{controller=Home}/{action=Index}/{id?}" :
Correspond à un chemin d’accès d’URL comme
/Products/Details/5Extrait les valeurs de route
{ controller = Products, action = Details, id = 5 }en segmentant le chemin d’accès en jetons. L’extraction des valeurs de route entraîne une correspondance si l’application a un contrôleur nomméProductsControlleret uneDetailsaction :public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche les informations de routage.
/Products/Details/5modèle associe la valeur deid = 5au paramètreidpour lui attribuer la valeur5. Pour plus d’informations, consultez la liaison de modèles.{controller=Home}définitHomecommecontrollerpar défaut.{action=Index}définitIndexcommeactionpar défaut.Le caractère
?dans{id?}définitidcomme facultatif.- Les paramètres de route par défaut et facultatifs n’ont pas besoin d’être présents dans le chemin d’URL pour qu’une correspondance soit établie. Pour obtenir une description détaillée de la syntaxe du modèle de routage, consultez Référence du modèle de routage.
Correspond au chemin de l’URL
/.Produit les valeurs d’itinéraire
{ controller = Home, action = Index }.
Les valeurs de controller et action utilisent les valeurs par défaut.
id ne produit pas de valeur, car il n’existe pas de segment correspondant dans le chemin d’accès d’URL.
/ Correspond uniquement s’il existe une HomeController action et Index :
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
À l’aide de la définition de contrôleur précédente et du modèle de routage, l’action HomeController.Index s’exécute pour les chemins d’URL suivants :
/Home/Index/17/Home/Index/Home/
Le chemin d’URL / utilise le contrôleur par défaut Home et l’action par défaut Index du modèle de routage. Le chemin d’accès d’URL /Home utilise l’action par défaut Index du modèle de route.
La méthode utilitaire MapDefaultControllerRoute :
app.MapDefaultControllerRoute();
Replaces:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Important
Le routage est configuré à l’aide des intergiciels UseRouting et UseEndpoints. Pour utiliser des contrôleurs :
- Appelez MapControllers pour mapper les contrôleurs à routage par attributs.
- Appelez MapControllerRoute ou MapAreaControllerRoute, pour mapper à la fois les contrôleurs routés de manière conventionnelle et les contrôleurs routés d’attribut .
Les applications n’ont généralement pas besoin d’appeler UseRouting ou UseEndpoints.
WebApplicationBuilder configure un pipeline d’intergiciels qui encapsule l’intergiciel ajouté dans Program.cs à l’aide de UseRouting et UseEndpoints. Pour plus d’informations, consultez Routing dans ASP.NET Core.
Routage conventionnel
Utilisez le routage conventionnel avec des contrôleurs et des vues. L’itinéraire default est le suivant :
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Le code précédent est un exemple d’itinéraire conventionnel. Nous appelons ce style routage conventionnel, car il établit une convention pour les chemins d’URL :
- Le premier segment de tracé,
{controller=Home}, correspond au nom du contrôleur. - Le deuxième segment,
{action=Index}, correspond au nom de l’action. - Le troisième segment,
{id?}est utilisé pour un facultatifid. Le?dans{id?}le rend facultatif.idcorrespond à une entité du modèle.
En utilisant cette route default, le chemin de l’URL :
-
/Products/Listcorrespond à l’actionProductsController.List. -
/Blog/Article/17mappe àBlogController.Articleet le modèle lie généralement leidparamètre à 17.
Ce mappage :
- Est basé uniquement sur les noms du contrôleur et des actions.
- N’est pas basé sur des espaces de noms, des emplacements de fichiers sources ou des paramètres de méthode.
En utilisant le routage conventionnel avec l’itinéraire par défaut, vous n’avez pas besoin de créer un modèle d’URL pour chaque action. Pour une application avec des actions de style CRUD , ayant une cohérence pour les URL entre les contrôleurs :
- Permet de simplifier le code.
- Rend l’interface utilisateur plus prévisible.
Warning
Le modèle d’itinéraire définit la id valeur facultative. Les actions peuvent s’exécuter sans l’ID facultatif fourni dans le cadre de l’URL. En règle générale, quand id est omis de l’URL :
- La liaison de modèle définit
idsur0. - Aucune entité n’est trouvée dans la base de données correspondant à
id == 0.
Le routage par attribut offre un contrôle précis permettant de rendre l’ID obligatoire pour certaines actions, mais pas pour d’autres. Par convention, la documentation inclut des paramètres facultatifs comme id quand ils sont susceptibles d’apparaître dans une utilisation correcte.
La plupart des applications doivent choisir un schéma de routage de base et descriptif pour que les URL soient lisibles et explicites. La route conventionnelle par défaut {controller=Home}/{action=Index}/{id?} :
- Prend en charge un schéma de routage de base et descriptif.
- Est un point de départ pratique pour les applications basées sur une interface utilisateur.
- Est le seul modèle d’itinéraire nécessaire pour de nombreuses applications d’interface utilisateur web. Pour les applications d’interface utilisateur web plus volumineuses, une autre route utilisant Zones est souvent tout ce qui est nécessaire.
MapControllerRoute et MapAreaRoute :
- Attribuer automatiquement une valeur order à leurs terminaisons en fonction de l’ordre dans lequel elles sont invoquées.
Routage des points de terminaison dans ASP.NET Core :
- N’a pas de concept de routes.
- Ne fournit pas de garanties d'ordre pour l’exécution de l’extensibilité. Tous les points de terminaison sont traités simultanément.
Activez la journalisation pour voir comment les implémentations de routage intégrées, comme Route, établissent des correspondances avec les requêtes.
Le routage des attributs est expliqué plus loin dans ce document.
Plusieurs routes conventionnelles
Vous pouvez configurer plusieurs itinéraires conventionnels en ajoutant d’autres appels à MapControllerRoute et MapAreaControllerRoute. Lorsque vous ajoutez ces appels, vous pouvez définir plusieurs conventions ou ajouter des itinéraires conventionnels dédiés à une action spécifique, par exemple :
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
La routeblog dans le code précédent est une route conventionnelle dédiée. Il s’agit d’une route conventionnelle dédiée, car :
- Elle utilise un routage conventionnel.
- C’est dédié à une action spécifique.
Étant donné que le modèle de route "blog/{*article}" n’inclut pas controller et action en tant que paramètres :
- Ils peuvent uniquement avoir les valeurs
{ controller = "Blog", action = "Article" }par défaut . - Cette route correspond toujours à l’action
BlogController.Article.
/Blog, /Blog/Articleet /Blog/{any-string} sont les seuls chemins d’URL qui correspondent à la route du blog.
Dans l’exemple précédent :
- L’itinéraire
bloga une priorité plus élevée pour les correspondances que l’itinérairedefault, car vous l’ajoutez en premier. - Il s’agit d’un exemple de routage de style Slug où il est typique d’avoir un nom d’article dans le cadre de l’URL.
Warning
Dans ASP.NET Core, le routage n'est pas :
- Définissez un concept appelé route.
UseRoutingajoute la mise en correspondance des routes au pipeline de middleware. L’UseRoutingintergiciel examine l’ensemble des points de terminaison définis dans l’application et sélectionne la meilleure correspondance des points de terminaison en fonction de la requête. - Fournissez des garanties sur l’ordre d’exécution de l’extensibilité, comme IRouteConstraint ou IActionConstraint.
Consultez Routage pour obtenir des informations de référence sur le routage.
Ordre de routage conventionnel
Le routage conventionnel correspond uniquement à une combinaison d’actions et de contrôleurs que l’application définit. Cette approche simplifie les cas où les itinéraires conventionnels se chevauchent.
Lorsque vous ajoutez des routes à l’aide de MapControllerRoute, MapDefaultControllerRoute et MapAreaControllerRoute, les points de terminaison reçoivent automatiquement une valeur d’ordre en fonction de l’ordre dans lequel vous appelez ces méthodes. Les correspondances à partir d’un itinéraire qui apparaît précédemment dans la liste ont une priorité plus élevée. Le routage conventionnel dépend de l’ordre. En général, placez des itinéraires avec des zones plus tôt, car ils sont plus spécifiques que les itinéraires sans zone.
Les routes conventionnelles dédiées avec des paramètres de route fourre-tout, tels que {*article}, peuvent rendre une route trop générique. Un itinéraire gourmand correspond aux URL que vous avez conçues pour être mises en correspondance par d’autres itinéraires. Mettez les routes gourmandes plus loin dans la table de routage pour éviter les correspondances gourmandes.
Warning
Un paramètre catch-all peut correspondre incorrectement aux itinéraires en raison d’un bogue de routage. Les applications affectées par ce bogue présentent les caractéristiques suivantes :
- Un routage catch-all, par exemple,
{**slug}" - Le routage catch-all ne correspond pas aux requêtes auxquelles il devrait correspondre.
- La suppression d’autres routages fait que le routage de type catch-all commence à fonctionner.
Consultez les bogues de GitHub 18677 et 16579 pour des cas d'exemple affectés par ce bogue.
Un correctif d’opt-in pour ce bogue est contenu dans .NET Core 3.1.301 ou version ultérieure sdk. Le code suivant définit un commutateur interne qui corrige ce bogue :
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Résoudre les actions ambiguës
Lorsque deux points de terminaison correspondent via le routage, le routage doit effectuer l’une des étapes suivantes :
- Choisissez le meilleur candidat.
- Levée d'une exception.
Par exemple:
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);
}
}
Le contrôleur précédent définit deux actions qui correspondent :
- Le chemin d’URL
/Products33/Edit/17 - Données de route
{ controller = Products33, action = Edit, id = 17 }.
Il s’agit d’un modèle classique pour les contrôleurs MVC :
-
Edit(int)affiche un formulaire pour modifier un produit. -
Edit(int, Product)traite le formulaire publié.
Pour déterminer l'itinéraire correct :
-
Edit(int, Product)est sélectionné lorsque la requête est un httpPOST. -
Edit(int)est sélectionné lorsque le verbe HTTP est autre chose.Edit(int)est généralement appelé par le biais deGET.
Le HttpPostAttribute, [HttpPost], est fourni pour le routage afin qu’il puisse choisir en fonction de la méthode HTTP de la requête. Le HttpPostAttribute rend Edit(int, Product) plus adapté que Edit(int).
Il est important de comprendre le rôle des attributs tels que HttpPostAttribute. Des attributs similaires sont définis pour d’autres verbes HTTP. Dans le routage conventionnel, les actions utilisent souvent le même nom d’action lorsqu’elles font partie d’un flux de travail d’affichage et d’envoi de formulaire. Par exemple, consultez Examiner les deux méthodes d’action Modifier.
Si le routage ne peut pas choisir le meilleur candidat, il génère une exception AmbiguousMatchException et répertorie les différents points de terminaison correspondants.
Noms de routes conventionnelles
Les chaînes "blog" et "default" dans les exemples suivants sont des noms de routes conventionnelles :
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Les noms de routes donnent à la route un nom logique. La route nommée peut être utilisée pour la génération d’URL. Utiliser une route nommée simplifie considérablement la création d’URL quand l’ordonnancement des routes peut rendre compliquée la génération des URL. Les noms de routes doivent être unique à l’échelle de l’application.
Noms d’itinéraires :
- N’ont aucun impact sur la correspondance d’URL ou la gestion des requêtes.
- Sont utilisés uniquement pour la génération d’URL.
Le concept de nom de route est représenté dans le routage sous la forme IEndpointNameMetadata. Les termes nom de la route et nom du point de terminaison :
- Sont interchangeables.
- Celui qui est utilisé dans la documentation et le code dépend de l’API décrite.
Routage d’attributs pour les API REST
Les API REST doivent utiliser le routage d’attributs pour modéliser les fonctionnalités de l’application sous la forme d’un ensemble de ressources dans lequel les opérations sont représentées par des verbes HTTP.
Le routage par attributs utilise un ensemble d’attributs pour mapper les actions directement aux modèles de routes. Le code suivant est classique pour une REST API et est utilisé dans l’exemple suivant :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Dans le code précédent, vous appelez MapControllers pour mapper les contrôleurs avec routage par attributs.
Dans l’exemple suivant :
-
HomeControllercorrespond à un ensemble d’URL similaire à celui auquel la route conventionnelle par défaut{controller=Home}/{action=Index}/{id?}correspond.
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);
}
}
L’action HomeController.Index s’exécute pour l’un des chemins d’URL suivants : /, /Home, /Home/Index ou /Home/Index/3.
Cet exemple met en évidence une différence importante en termes de programmation entre le routage par attributs et le routage conventionnel. Le routage d’attributs nécessite plus d’entrée pour spécifier une route. La route par défaut conventionnelle gère les itinéraires de manière plus succincte. Cependant, le routage par attributs permet (et nécessite) un contrôle précis des modèles de routes qui s’appliquent à chaque action.
Avec le routage par attributs, les noms du contrôleur et de l’action n’entrent pas en ligne de compte dans la détermination de l’action mise en correspondance, sauf si le remplacement de jetons est utilisé. L’exemple suivant correspond aux mêmes URL que l’exemple précédent :
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);
}
}
Le code suivant utilise la substitution de jetons pour action et 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();
}
}
Le code suivant applique [Route("[controller]/[action]")] au contrôleur :
[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();
}
}
Dans le code précédent, les modèles de méthode Index doivent préfixer les modèles de route avec / ou ~/. Les modèles de routes appliqués à une action qui commencent par / ou ~/ ne sont pas combinés avec les modèles de routes appliqués au contrôleur.
Pour plus d’informations sur la sélection du modèle de route, consultez Priorité du modèle de route.
Noms de routage réservés
Les mots clés suivants sont des noms de paramètres de route réservés lorsque vous utilisez les contrôleurs ou les pages Razor :
actionareacontrollerhandlerpage
L’utilisation page comme paramètre d’itinéraire avec le routage d’attributs est une erreur courante. Ce choix entraîne un comportement incohérent et confus avec la génération d’URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
La génération d’URL utilise ces noms de paramètres spéciaux pour déterminer si une opération de génération d’URL fait référence à une Razor page ou à un contrôleur.
Les mots clés suivants sont réservés dans le contexte d’une Razor vue ou d’une Razor page :
pageusingnamespaceinjectsectioninheritsmodeladdTagHelperremoveTagHelper
N’utilisez pas ces mots clés pour les générations de liens, les paramètres liés au modèle ou les propriétés de niveau supérieur.
Modèles de verbes HTTP
ASP.NET Core inclut les modèles de verbe HTTP suivants :
Modèles de routage
ASP.NET Core inclut les modèles d’itinéraire suivants :
- Tous les modèles de verbe HTTP sont des modèles de routage.
- [Route]
Routage d’attributs avec des attributs de verbe HTTP
Examinons le contrôleur ci-dessous :
[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);
}
}
Dans le code précédent :
- Chaque action contient l’attribut
[HttpGet], qui limite la correspondance aux requêtes HTTP GET uniquement. - L’action
GetProductinclut le modèle"{id}", de sorte queidest ajouté au modèle"api/[controller]"dans le contrôleur. Le modèle de la méthode est"api/[controller]/{id}". Par conséquent, cette action correspond uniquement aux requêtes GET pour le formulaire/api/test2/xyz,/api/test2/123,/api/test2/{any string}et ainsi de suite.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); } - L’action
GetIntProductcontient le modèle"int/{id:int}". La portion:intdu modèle limite les valeurs de routeidaux chaînes pouvant être converties en un entier. Une requête GET vers/api/test2/int/abc:- Ne correspond pas à cette action.
- Retourne une erreur 404 Introuvable.
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- L’action
GetInt2Productcontient{id}dans le modèle, mais ne limiteidpas les valeurs qui peuvent être converties en entier. Une requête GET vers/api/test2/int2/abc:- Correspond à cette route.
- La liaison de modèle ne parvient pas à convertir
abcen nombre entier. Le paramètreidde la méthode est entier. - Retourne une 400 Requête incorrecte, car la liaison de modèle n’a pas pu convertir
abcen un entier.[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Le routage par attributs peut utiliser des attributs HttpMethodAttribute tels que HttpPostAttribute, HttpPutAttribute, et HttpDeleteAttribute. Tous les attributs de verbe HTTP acceptent un modèle de route. L’exemple suivant montre deux actions qui correspondent au même modèle de route :
[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);
}
}
À l’aide du chemin d’accès d’URL /products3 :
- L’action
MyProductsController.ListProductss’exécute lorsque le verbe HTTP estGET. - L’action
MyProductsController.CreateProducts’exécute lorsque le verbe HTTP estPOST.
Lors de la création d’une REST API, il est rare que vous deviez utiliser [Route(...)] sur une méthode d’action, car l’action accepte toutes les méthodes HTTP. Utilisez l’attribut de verbe HTTP plus spécifique pour être précis sur ce que votre API prend en charge. Les clients des REST API doivent normalement connaître les chemins et les verbes HTTP qui correspondent à des opérations logiques spécifiques.
REST API doivent utiliser le routage d’attributs pour modéliser les fonctionnalités de l’application sous la forme d’un ensemble de ressources dans lequel les opérations sont représentées par des verbes HTTP. Cette conception signifie que de nombreuses opérations, telles que GET et POST sur la même ressource logique, utilisent la même URL. Le routage d’attributs fournit le niveau de contrôle nécessaire pour concevoir soigneusement la disposition du point de terminaison public d’une API.
Dans la mesure où une route d’attribut s’applique à une action spécifique, il est facile de placer les paramètres nécessaires dans la définition du modèle de route. Dans l’exemple suivant, id est obligatoire dans le chemin d’accès d’URL :
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
L’action Products2ApiController.GetProduct(int) :
- S’exécute avec un chemin d’URL tel que
/products2/3 - Ne s’exécute pas avec le chemin d’URL
/products2.
L’attribut [Consumes] permet à une action de limiter les types de contenu de la demande pris en charge. Pour plus d’informations, consultez Définir les types de contenu de la requête pris en charge avec l’attribut [Consumes].
Consultez Routage pour obtenir une description complète des modèles de routes et des options associées.
Pour plus d’informations sur [ApiController], consultez Attribut ApiController.
Nom de l’itinéraire
Le code suivant définit un nom de route Products_List :
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Utilisez des noms de routage pour générer une URL basée sur un itinéraire spécifique. Noms d’itinéraires :
- N’ont aucun impact sur le comportement de correspondance d’URL du routage.
- Sont utilisés uniquement pour la génération d’URL.
Les noms de routes doivent être unique à l’échelle de l’application.
Comparez le code précédent avec la route par défaut conventionnelle, qui définit le paramètre id comme étant facultatif ({id?}). La possibilité de spécifier les API avec précision présente des avantages, par exemple de permettre de diriger /products et /products/5 vers des actions différentes.
Combiner des routes d’attributs
Pour rendre le routage des attributs moins répétitif, combinez les attributs de routage sur le contrôleur avec des attributs de routage sur les actions individuelles. Les modèles de route que vous définissez sur le contrôleur sont ajoutés en préfixe aux modèles de route des actions. Lorsque vous placez un attribut de routage sur le contrôleur, toutes les actions du contrôleur utilisent le routage des attributs.
[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);
}
}
Dans l’exemple précédent :
- Le chemin d’URL
/productspeut correspondre àProductsApi.ListProducts - Le chemin d’accès de l’URL
/products/5peut correspondre àProductsApi.GetProduct(int).
Ces deux actions correspondent seulement à HTTP GET, car elles sont marquées avec l’attribut [HttpGet].
Modèles de routage que vous appliquez à une action et qui commencent par / ou ~/ ne sont pas combinés avec les modèles de routage que vous appliquez au contrôleur. L'exemple suivant correspond à un ensemble de chemins d'accès URL similaires à la route par défaut.
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Le tableau suivant décrit les [Route] attributs dans le code précédent.
| Attribute | Combine avec [Route("Home")] |
Définit le modèle de route |
|---|---|---|
[Route("")] |
Yes | "Home" |
[Route("Index")] |
Yes | "Home/Index" |
[Route("/")] |
Non | "" |
[Route("About")] |
Yes | "Home/About" |
Ordre de route des attributs
Le routage génère une arborescence et fait correspondre simultanément tous les points de terminaison.
- Les entrées d’itinéraire se comportent comme si elles sont placées dans un ordre idéal.
- Les routes les plus spécifiques sont susceptibles d'être exécutées avant les routes plus générales.
Par exemple, une route d’attribut comme blog/search/{topic} est plus spécifique qu’une route d’attribut comme blog/{*article}. La route blog/search/{topic} a une priorité plus élevée, par défaut, car elle est plus spécifique. En utilisant le routage conventionnel, le développeur est chargé de placer des itinéraires dans l’ordre souhaité.
Les routes d’attributs peuvent configurer un ordre à l’aide de la propriété Order. Tous les attributs de route fournis par le framework incluent Order . Les routes sont traitées selon un ordre croissant de la propriété Order. L’ordre par défaut est 0. La définition d’une route avec Order = -1 fait que cette route s’exécute avant les routes qui ne définissent pas d’ordre. Définir une route à l’aide de Order = 1 s’effectue après l’application de l’ordre de routage par défaut.
Évitez de dépendre de Order. Si l’espace d’URL d’une application nécessite des valeurs d’ordre explicites pour router correctement, il est probable qu’il prête également à confusion pour les clients. D’une façon générale, le routage par attributs sélectionne la route correcte avec la mise en correspondance d’URL. Si l’ordre par défaut utilisé pour la génération d’URL ne fonctionne pas, l’utilisation à titre de remplacement d’un nom de route est généralement plus simple que d’appliquer la propriété Order.
Considérez les deux contrôleurs suivants qui définissent tous deux la route correspondant à /home :
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);
}
}
Le fait de demander /home à l’aide du code précédent lève une exception semblable à la suivante :
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
L’ajout Order à l’un des attributs de route résout l’ambiguïté :
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
Avec le code précédent, /home exécute le point de terminaison HomeController.Index. Pour accéder à MyDemoController.MyIndex, demandez /home/MyIndex.
Remarque :
- Le code précédent est un exemple de conception de routage médiocre. Il présente la propriété
Order. - La
Orderpropriété résout uniquement l’ambiguïté. Ce modèle ne peut pas être mis en correspondance. Il est préférable de supprimer le[Route("Home")]modèle.
Pour plus d’informations sur l’ordre des routes avec Razor Pages, consultez Razor les conventions de routage et d’application de Pages : Ordre des routes.
Dans certains cas, une erreur HTTP 500 est retournée avec des routes ambiguës. Utilisez la journalisation pour voir quels points de terminaison sont à l’origine de AmbiguousMatchException.
Remplacement de jetons dans les modèles de route [contrôleur], [action], [zone]
Pour plus de commodité, les routes définies par attribut prennent en charge leremplacement de jetons, en entourant un jeton de crochets ([, ]). Les jetons [action], [area]et [controller] sont remplacés par les valeurs du nom de l’action, du nom de la zone et du nom du contrôleur à partir de l’action où vous définissez l’itinéraire :
[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);
}
}
Dans le code précédent :
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Correspondances
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Correspondances
/Products0/Edit/{id}
Le remplacement des jetons a lieu lors de la dernière étape de la création des routes définies par attributs. L’exemple précédent se comporte de la même manière que le code suivant :
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);
}
}
Si vous lisez cela dans une langue autre que l'anglais, faites-le nous savoir dans cette GitHub problème de discussion si vous souhaitez voir les commentaires de code dans votre langue native.
Vous pouvez également combiner des itinéraires d’attributs avec l’héritage. Cette combinaison est puissante lorsque vous utilisez le remplacement de symboles. Le remplacement des jetons s’applique aussi aux noms de routes définis par des routes d’attribut.
[Route("[controller]/[action]", Name="[controller]_[action]")] génère un nom de route unique pour chaque action :
[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);
}
}
Pour faire correspondre littéralement au délimiteur de remplacement de jeton [ ou ], placez-le en échappement en répétant le caractère ([[ ou ]]).
Utiliser un transformateur de paramètre pour personnaliser le remplacement des jetons
Vous pouvez personnaliser le remplacement de jeton à l’aide d’un transformateur de paramètres. Un transformateur de paramètre implémente IOutboundParameterTransformer et transforme la valeur des paramètres. Par exemple, un transformateur de paramètre SlugifyParameterTransformer personnalisé transforme la valeur de la route SubscriptionManagement en 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();
}
}
RouteTokenTransformerConvention est une convention de modèle d’application qui :
- Appliquez un transformateur de paramètres à tous les itinéraires d’attribut dans une application.
- Personnalise les valeurs des jetons des routes définies par attributs lors de leur remplacement.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
La méthode ListAllprécédente correspond à /subscription-management/list-all.
Le RouteTokenTransformerConvention est inscrit en tant qu’option :
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();
Pour obtenir la définition de slug, consultez la documentation web MDN sur Slug.
Warning
Lorsque vous utilisez System.Text.RegularExpressions pour traiter une entrée non approuvée, passez un délai d’expiration. Un utilisateur malveillant peut fournir une entrée à RegularExpressions, provoquant une attaque par déni de service. Les API d’infrastructure ASP.NET Core qui utilisent RegularExpressions passent un délai d’expiration.
Routes à attributs multiples
Le routage par attributs prend en charge la définition de plusieurs routes pour atteindre la même action. L’utilisation la plus courante de ceci est d’imiter le comportement de la route conventionnelle par défaut, comme le montre l’exemple suivant :
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
Le fait de placer plusieurs attributs de route sur le contrôleur signifie que chacun d’eux se combine avec chacun des attributs de route sur les méthodes d’action :
[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();
}
}
Toutes les contraintes de routes de verbe HTTP implémentent IActionConstraint.
Lorsque plusieurs attributs de route qui implémentent IActionConstraint sont placés sur une action :
- Chaque contrainte d’action se combine avec le modèle de route appliqué au contrôleur.
[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();
}
}
L’utilisation de plusieurs routes sur des actions peut sembler utile et puissante. Il est préférable de conserver l’espace URL de votre application de base et bien défini. Utilisez plusieurs routes sur les actions seulement là où c’est nécessaire, par exemple pour prendre en charge des clients existants.
Spécification facultative de paramètres, de valeurs par défaut et de contraintes pour les routes d’attribut
Les routes d’attribut prennent en charge la même syntaxe inline que les routes conventionnelles pour spécifier des paramètres, des valeurs par défaut et des contraintes facultatifs.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Dans le code précédent, [HttpPost("product14/{id:int}")] applique une contrainte de route. L’action Products14Controller.ShowProduct est mise en correspondance uniquement par des chemins d’accès d’URL tels que /product14/3. La partie {id:int} modèle de routage limite ce segment à des entiers uniquement.
Pour obtenir une description détaillée de la syntaxe du modèle de routage, consultez Référence du modèle de routage.
Attributs de route personnalisés à l’aide de IRouteTemplateProvider
Tous les attributs de route implémentent IRouteTemplateProvider. L'environnement d'exécution ASP.NET Core :
- Recherche des attributs sur les classes de contrôleur et les méthodes d’action au démarrage de l’application.
- Utilise les attributs qui implémentent
IRouteTemplateProviderpour générer l’ensemble initial de routes.
Implémentez IRouteTemplateProvider pour définir des attributs de route personnalisés. Chaque IRouteTemplateProvider vous permet de définir une route avec un modèle, un nom et un ordre de route personnalisés :
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();
}
}
La méthode précédente Get retourne Order = 2, Template = api/MyTestApi.
Utiliser le modèle d’application pour personnaliser les routes des attributs
Le modèle d'application
- Modèle objet créé au démarrage dans
Program.cs. - Contient toutes les métadonnées utilisées par ASP.NET Core pour acheminer et exécuter les actions dans une application.
Le modèle d’application inclut toutes les données collectées à partir des attributs de route. Les données des attributs de routage sont fournies par l’implémentation IRouteTemplateProvider. Conventions:
- Peut être écrit pour modifier le modèle d’application afin de personnaliser le comportement du routage.
- Sont lus au démarrage de l’application.
Cette section présente un exemple de base de personnalisation du routage à l’aide du modèle d’application. Le code suivant aligne approximativement les routes avec la structure de dossiers du projet.
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()
};
}
}
}
Le code suivant empêche l’application de la namespace convention aux contrôleurs qui sont routés par l’attribut :
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Par exemple, le contrôleur suivant n’utilise pas 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}");
}
}
La méthode NamespaceRoutingConvention.Apply :
- Ne fait rien si le contrôleur utilise le routage par attributs.
- Définit le modèle de contrôleurs en fonction de
namespace, avec la basenamespacesupprimée.
Le NamespaceRoutingConvention peut être appliqué dans 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();
Par exemple, prenons le contrôleur suivant :
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}");
}
}
}
Dans le code précédent :
- La base
namespaceestMy.Application. - Le nom complet du contrôleur précédent est
My.Application.Admin.Controllers.UsersController. - Le
NamespaceRoutingConventiondéfinit le modèle de contrôleurs surAdmin/Controllers/Users/[action]/{id?.
Le NamespaceRoutingConvention peut également être appliqué en tant qu’attribut sur un contrôleur :
[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}");
}
}
Routage mixte : routage conventionnel et routage par attributs
ASP.NET Core applications peuvent combiner l’utilisation du routage conventionnel et du routage d’attributs. En général, vous utilisez le routage conventionnel pour les contrôleurs qui renvoient des pages HTML aux navigateurs, et le routage par attribut pour les contrôleurs qui exposent des API REST.
Les actions sont routées de façon conventionnelle ou routées par attribut. Lorsque vous définissez une route sur le contrôleur ou l’action, elle devient une route par attribut. Vous ne pouvez pas accéder aux actions qui définissent des routes par attribut via les routes conventionnelles, et vice versa. Touteaction acheminée par l’attributallroute du contrôleur.
Le routage d’attributs et le routage conventionnel utilisent le même moteur de routage.
Routage avec des caractères spéciaux
Le routage avec des caractères spéciaux peut entraîner des résultats inattendus. Par exemple, considérez un contrôleur avec la méthode d’action suivante :
[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null || todoItem.Name == null)
{
return NotFound();
}
return todoItem.Name;
}
Lorsque string id contient les valeurs encodées suivantes, des résultats inattendus peuvent se produire :
| ASCII | Encoded |
|---|---|
/ |
%2F |
|
+ |
Les paramètres de routage ne sont pas toujours décodés par URL. Ce problème peut être résolu à l’avenir. Pour plus d’informations, consultez ce problème GitHub ;
Génération d’URL et valeurs ambiantes
Les applications peuvent utiliser les fonctionnalités de génération d’URL de routage pour générer des liens URL vers des actions. La génération d’URL élimine le codage en dur des URL, ce qui rend un code plus robuste et plus facile à maintenir. Cette section se concentre sur les fonctionnalités de génération d’URL fournies par MVC et couvre seulement les principes de base du fonctionnement de la génération d’URL. Pour une description détaillée de la génération d’URL, consultez Routage.
L’interface IUrlHelper est l’élément d’infrastructure sous-jacent entre MVC et le routage pour la génération d’URL. Une instance de IUrlHelper est disponible par le biais de la propriété Url dans les contrôleurs, les vues et les composants de vue.
Dans l’exemple suivant, l’interface IUrlHelper est utilisée par le biais de la propriété Controller.Url pour générer une URL vers une autre action.
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();
}
}
Si l’application utilise l’itinéraire classique par défaut, la valeur de la url variable est la chaîne /UrlGeneration/Destinationde chemin d’URL. Le routage crée ce chemin d’URL en combinant :
- Valeurs de routage de la requête actuelle, appelées valeurs ambiantes.
- Valeurs transmises à
Url.Actionet en remplaçant ces valeurs dans le modèle de route :
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
Dans le modèle de route, chaque paramètre de route a sa valeur remplacée en faisant correspondre les noms aux valeurs et aux valeurs ambiantes. Un paramètre de route qui n’a pas de valeur peut :
- Utilisez une valeur par défaut s’il en a une.
- Peut être ignoré si c'est facultatif. Par exemple, le
idissu du modèle de route{controller}/{action}/{id?}.
La génération d’URL échoue si un paramètre de route obligatoire n’a pas de valeur correspondante. Si la génération d’URL échoue pour une route, la route suivante est essayée, ceci jusqu’à ce que toutes les routes aient été essayées ou qu’une correspondance soit trouvée.
L’exemple précédent de Url.Action suppose le routage conventionnel. La génération d’URL fonctionne de façon similaire avec le routage par attributs, même si les concepts sont différents. Avec un routage conventionnel :
- Les valeurs de route sont utilisées pour développer un modèle.
- Les valeurs de routage pour
controlleretactions’affichent généralement dans ce modèle. Cela fonctionne, car les URL mises en correspondance par le routage respectent une convention.
L’exemple suivant utilise un attribut de routage :
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();
}
}
L’action Source dans le code précédent génère custom/url/to/destination.
LinkGenerator a été ajouté dans ASP.NET Core 3.0 comme alternative à IUrlHelper.
LinkGenerator offre des fonctionnalités similaires, mais plus flexibles. Chaque méthode sur IUrlHelper a également une famille de méthodes correspondante sur LinkGenerator.
Génération des URL par nom d’action
Url.Action, LinkGenerator.GetPathByAction et toutes les surcharges associées sont tous conçus pour générer le point de terminaison cible en spécifiant un nom de contrôleur et un nom d’action.
Lorsque vous utilisez Url.Action, le runtime fournit les valeurs d’itinéraire actuelles pour controller et action:
- Les valeurs de
controlleret deactionfont partie à la fois des valeurs ambiantes et des valeurs. La méthodeUrl.Actionutilise toujours les valeurs actuelles deactionet decontroller, et génère un chemin d’accès d’URL qui route vers l’action actuelle.
Le routage essaye d’utiliser les valeurs dans les valeurs ambiantes pour renseigner les informations qui n’ont pas été fournies lors de la génération d’une URL. Considérez une route comme {a}/{b}/{c}/{d} avec des valeurs ambiantes { a = Alice, b = Bob, c = Carol, d = David } :
- Le routage contient suffisamment d’informations pour générer une URL sans valeurs supplémentaires.
- Le routage contient suffisamment d’informations, car tous les paramètres de route ont une valeur.
Si la valeur { d = Donovan } est ajoutée :
- La valeur
{ d = David }est ignorée. - Le chemin d’accès d’URL généré est
Alice/Bob/Carol/Donovan.
Avertissement : Les chemins d’accès d’URL sont hiérarchiques. Dans l’exemple précédent, si la valeur { c = Cheryl } est ajoutée :
- Les deux valeurs
{ c = Carol, d = David }sont ignorées. - Il n’y a plus de valeur pour
det la génération d’URL échoue. - Vous devez spécifier les valeurs souhaitées de
cet dedpour générer une URL.
Vous pouvez vous attendre à rencontrer ce problème avec l’itinéraire {controller}/{action}/{id?}par défaut. Ce problème est rare dans la pratique, car Url.Action spécifie toujours explicitement une controller valeur et action.
Plusieurs surcharges de Url.Action prennent un objet de valeurs d’itinéraire pour fournir des valeurs pour les paramètres de route autres que controller et action. L’objet valeurs de route est fréquemment utilisé avec id. Par exemple : Url.Action("Buy", "Products", new { id = 17 }). L'objet des valeurs de route
- Par convention est généralement un objet de type anonyme.
- Il peut s’agir d’un
IDictionary<>ou d’un POCO).
Toutes les valeurs d’itinéraire supplémentaires qui ne correspondent pas aux paramètres d’itinéraire vont dans la chaîne de requête.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url!);
}
Le code précédent génère /Products/Buy/17?color=red.
Le code suivant génère une URL absolue :
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!);
}
Pour créer une URL absolue, utilisez l’une des options suivantes :
- Une surcharge qui accepte un
protocol. Par exemple, le code précédent. - LinkGenerator.GetUriByAction, qui génère des URI absolus par défaut.
Générer des URL par route
Le code précédent a montré la génération d’une URL en passant le nom du contrôleur et le nom de l’action.
IUrlHelper fournit également la famille de méthodes Url.RouteUrl. Ces méthodes sont similaires à Url.Action, mais elle ne copient pas les valeurs actuelles de action et de controller vers les valeurs de route. Utilisation la plus courante pour Url.RouteUrl :
- Spécifie un nom d’itinéraire pour générer l’URL.
- En règle générale, ne spécifie pas de contrôleur ou de nom d’action.
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();
}
Le fichier suivant Razor génère un lien HTML versDestination_Route :
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Générer des URL en HTML et Razor
IHtmlHelper fournit les HtmlHelperméthodes Html.BeginForm et Html.ActionLink pour générer <form> et <a> éléments respectivement. Ces méthodes utilisent la méthode Url.Action pour générer une URL et ils acceptent les arguments similaires. Les pendants de Url.RouteUrl pour HtmlHelper sont Html.BeginRouteForm et Html.RouteLink, qui ont des fonctionnalités similaires.
Les TagHelpers génèrent des URL via le TagHelper form et le TagHelper <a>. Ils utilisent tous les deux IUrlHelper pour leur implémentation. Pour plus d’informations, consultez Tag Helpers dans les formulaires.
Dans les vues, la IUrlHelper propriété est disponible via la Url propriété pour toute génération d’URL ad hoc non couverte par les méthodes précédentes.
Génération d’URL dans les résultats d'action
Les exemples précédents montrent comment utiliser IUrlHelper dans un contrôleur. L’utilisation la plus courante dans un contrôleur consiste à générer une URL dans le cadre du résultat d’une action.
Les classes de base ControllerBase et Controller fournissent des méthodes pratiques pour les résultats d’action qui référencent une autre action. Une utilisation typique est de rediriger après acceptation de l’entrée utilisateur :
[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);
}
Les méthodes de fabrique de résultats d’action comme RedirectToAction et CreatedAtAction suivent un modèle similaire aux méthodes sur IUrlHelper.
Cas spécial pour les routes conventionnelles dédiées
Le routage conventionnel peut utiliser un type spécial de définition de route appelé route conventionnelle dédiée. Dans l’exemple suivant, la route nommée blog est une route conventionnelle dédiée :
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
À l’aide des définitions de routage précédentes, Url.Action("Index", "Home") génère le chemin / d’URL à l’aide de l’itinéraire default , mais pourquoi ? Vous pourriez supposer que les valeurs de route { controller = Home, action = Index } suffisent pour générer une URL en utilisant blog, et le résultat serait /blog?action=Index&controller=Home.
Les routes conventionnelles dédiées s’appuient sur un comportement spécial des valeurs par défaut qui n’ont pas de paramètre de route correspondant, ce qui empêche la route d’être trop gourmande lors de la génération d’URL. Dans ce cas, les valeurs par défaut sont { controller = Blog, action = Article }, et ni controller ni action n’apparaissent comme paramètre de route. Quand le routage effectue une génération d’URL, les valeurs fournies doivent correspondre aux valeurs par défaut. La génération d’URL avec blog échoue, car les valeurs { controller = Home, action = Index } ne correspondent pas à { controller = Blog, action = Article }. Le routage essaye alors d’utiliser default, ce qui réussit.
Areas
Les zones sont une fonctionnalité MVC utilisée pour organiser des fonctionnalités connexes dans un groupe distinct :
- Espace de noms de routage pour les actions du contrôleur.
- Structure des dossiers pour les vues.
L’utilisation de zones permet à une application d’avoir plusieurs contrôleurs portant le même nom, pour autant qu’ils soient dans des zones différentes. L’utilisation de zones crée une hiérarchie qui permet le routage par ajout d’un autre paramètre de route, area, à controller et à action. Cette section explique comment le routage interagit avec les zones. Consultez Zones pour savoir comment les zones sont utilisées avec les vues.
L’exemple suivant configure MVC pour utiliser la route conventionnelle par défaut et une route area pour un area nommé Blog :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
app.Run();
Dans le code précédent, MapAreaControllerRoute est appelé pour créer le "blog_route". Le deuxième paramètre, "Blog", est le nom de la zone.
Lors de la correspondance d’un chemin d’URL comme /Manage/Users/AddUser, la route "blog_route" génère les valeurs de route { area = Blog, controller = Users, action = AddUser }. La area valeur de route provient d’une valeur par défaut pour area. L’itinéraire créé par MapAreaControllerRoute est équivalent au code suivant :
app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
MapAreaControllerRoute crée un itinéraire en utilisant à la fois une valeur par défaut et une contrainte pour area, à l’aide du nom de zone fourni, dans ce cas Blog. La valeur par défaut garantit que l’itinéraire produit toujours { area = Blog, ... }, et la contrainte impose la valeur { area = Blog, ... } pour la génération d’URL.
Le routage conventionnel est dépendant de l’ordre. En général, placez les itinéraires avec des zones plus tôt, car ils sont plus spécifiques que les itinéraires sans zone.
Avec l’exemple précédent, les valeurs de route { area = Blog, controller = Users, action = AddUser } sont mises en correspondance avec l’action suivante :
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}");
}
}
}
L’attribut [Zone] indique qu’un contrôleur fait partie d’une zone. Ce contrôleur se trouve dans la zone Blog. Les contrôleurs sans l’attribut [Area] n’appartiennent à aucune zone et ne correspondent pas lorsque la valeur de route area est fournie par le mécanisme de routage. Dans l’exemple suivant, seul le premier contrôleur répertorié peut correspondre aux valeurs de route { area = Blog, controller = Users, action = AddUser }.
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace2
{
// Matches { area = Zebra, controller = Users, action = AddUser }
[Area("Zebra")]
public class UsersController : Controller
{
// GET /zebra/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace3
{
// Matches { area = string.Empty, controller = Users, action = AddUser }
// Matches { area = null, controller = Users, action = AddUser }
// Matches { controller = Users, action = AddUser }
public class UsersController : Controller
{
// GET /users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
Pour être exhaustif, l’espace de noms de chaque contrôleur est affiché ici. Si les contrôleurs précédents utilisaient le même espace de noms, une erreur du compilateur serait générée. Les espaces de noms de classe n’ont pas d’effet sur le routage de MVC.
Les deux premiers contrôleurs appartiennent à des zones et ne correspondent que lorsque le nom de leur zone respective est fourni par la valeur d’itinéraire area. Le troisième contrôleur n’est membre d’aucune zone et peut être trouvé en correspondance seulement quand aucune valeur pour area n’est fournie par le routage.
En termes de mise en correspondance avec aucune valeur, l’absence de la valeur area est identique à une valeur null ou de chaîne vide pour area.
Lors de l’exécution d’une action à l’intérieur d’une zone, la valeur de route pour area est disponible en tant que valeur ambiante, que le routage peut utiliser pour la génération d’URL. Cela signifie que, par défaut, les zones sont conservées lors de la génération d’URL, comme le montre l’exemple suivant.
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);
}
}
}
Le code suivant génère une URL vers /Zebra/Users/AddUser :
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Définition d’action
Les méthodes publiques sur un contrôleur, sauf celles qui sont avec l’attribut NonAction, sont des actions.
Exemple de code
- MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche les informations de routage.
- Afficher ou télécharger un exemple de code (comment télécharger)
Déboguer les diagnostics
Pour obtenir une sortie de diagnostic de routage détaillée, définissez Logging:LogLevel:Microsoft sur Debug. Dans l'environnement Development, définissez le niveau de journalisation dans appsettings.Development.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Les contrôleurs ASP.NET Core utilisent le middleware de routage pour faire correspondre les URL des requêtes entrantes et les mapper à des actions. Modèles d’itinéraire :
- Sont définies dans le code de démarrage ou dans des attributs.
- Décrire comment les chemins d’accès d’URL sont mis en correspondance avec les actions.
- Sont utilisés pour générer des URL pour les liens. Les liens générés sont généralement retournés dans les réponses.
Les actions sont routées de façon conventionnelle ou routées par attribut. Le fait d’appliquer une route au contrôleur ou à l’action active le routage par attributs. Pour plus d’informations, consultez Routage mixte.
Ce document :
- Explique les interactions entre MVC et le routage :
- Comment les applications MVC classiques utilisent les fonctionnalités de routage?
- Couvre les deux :
- Routage conventionnel généralement utilisé avec les contrôleurs et les vues.
- Routage d’attributs utilisé avec REST les API. Si vous êtes principalement intéressé par le routage pour REST les API, accédez à la section Routage des attributs pour REST les API.
- Pour plus d’informations sur le routage avancé, consultez Routage.
- Fait référence au système de routage par défaut ajouté dans ASP.NET Core 3.0, appelé routage de point de terminaison. Il est possible d’utiliser des contrôleurs avec la version précédente du routage à des fins de compatibilité. Pour obtenir des instructions, consultez le guide de migration 2.2-3.0. Reportez-vous à la version 2.2 de ce document pour obtenir des informations de référence sur le système de routage hérité.
Configurer la route conventionnelle
Startup.Configure a généralement du code similaire à l’exemple suivant lors de l’utilisation du routage conventionnel :
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Dans l’appel à UseEndpoints, MapControllerRoute est utilisé pour créer une seule route. La seule route est nommée default route. La plupart des applications qui utilisent des contrôleurs et des vues emploient un modèle de route similaire à la route default.
REST Les API doivent utiliser le routage d’attributs.
Le modèle de route "{controller=Home}/{action=Index}/{id?}" :
Correspond à un chemin d’accès d’URL comme
/Products/Details/5Extrait les valeurs de route
{ controller = Products, action = Details, id = 5 }en segmentant le chemin d’accès en jetons. L’extraction des valeurs de route entraîne une correspondance si l’application a un contrôleur nomméProductsControlleret uneDetailsaction :public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche les informations de routage.
/Products/Details/5modèle associe la valeur deid = 5au paramètreidpour lui attribuer la valeur5. Pour plus d’informations, consultez la liaison de modèles.{controller=Home}définitHomecommecontrollerpar défaut.{action=Index}définitIndexcommeactionpar défaut.Le caractère
?dans{id?}définitidcomme facultatif.Les paramètres de route par défaut et facultatifs n’ont pas besoin d’être présents dans le chemin d’URL pour qu’une correspondance soit établie. Pour obtenir une description détaillée de la syntaxe du modèle de routage, consultez Référence du modèle de routage.
Correspond au chemin de l’URL
/.Produit les valeurs d’itinéraire
{ controller = Home, action = Index }.
Les valeurs de controller et action utilisent les valeurs par défaut.
id ne produit pas de valeur, car il n’existe pas de segment correspondant dans le chemin d’accès d’URL.
/ Correspond uniquement s’il existe une HomeController action et Index :
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
À l’aide de la définition de contrôleur précédente et du modèle de routage, l’action HomeController.Index s’exécute pour les chemins d’URL suivants :
/Home/Index/17/Home/Index/Home/
Le chemin d’URL / utilise le contrôleur par défaut Home et l’action par défaut Index du modèle de routage. Le chemin d’accès d’URL /Home utilise l’action par défaut Index du modèle de route.
La méthode utilitaire MapDefaultControllerRoute :
endpoints.MapDefaultControllerRoute();
Replaces:
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
Important
Le routage est configuré à l’aide des composants middleware UseRouting, MapControllerRoute et MapAreaControllerRoute. Pour utiliser des contrôleurs :
- Appelez MapControllers dans
UseEndpointspour mapper les contrôleurs à routage par attribut. - Appelez MapControllerRoute ou MapAreaControllerRoute mappez les contrôleurs routés de manière conventionnelle et les contrôleurs routés par attribut .
Routage conventionnel
Utilisez le routage conventionnel avec des contrôleurs et des vues. L’itinéraire default est le suivant :
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Le code précédent est un exemple d’itinéraire conventionnel. Nous appelons ce style routage conventionnel, car il établit une convention pour les chemins d’URL :
- Le premier segment de tracé,
{controller=Home}, correspond au nom du contrôleur. - Le deuxième segment,
{action=Index}, correspond au nom de l’action. - Le troisième segment,
{id?}est utilisé pour un facultatifid. Le?dans{id?}le rend facultatif.idcorrespond à une entité du modèle.
En utilisant cette route default, le chemin de l’URL :
-
/Products/Listcorrespond à l’actionProductsController.List. -
/Blog/Article/17mappe àBlogController.Articleet le modèle lie généralement leidparamètre à 17.
Ce mappage :
- Est basé uniquement sur les noms du contrôleur et des actions.
- N’est pas basé sur des espaces de noms, des emplacements de fichiers sources ou des paramètres de méthode.
En utilisant le routage conventionnel avec l’itinéraire par défaut, vous n’avez pas besoin de créer un modèle d’URL pour chaque action. Pour une application avec des actions de style CRUD , ayant une cohérence pour les URL entre les contrôleurs :
- Permet de simplifier le code.
- Rend l’interface utilisateur plus prévisible.
Warning
Le modèle d’itinéraire définit la id valeur facultative. Les actions peuvent s’exécuter sans l’ID facultatif fourni dans le cadre de l’URL. En règle générale, quand id est omis de l’URL :
- La liaison de modèle définit
idsur0. - Aucune entité n’est trouvée dans la base de données correspondant à
id == 0.
Le routage par attribut offre un contrôle précis permettant de rendre l’ID obligatoire pour certaines actions, mais pas pour d’autres. Par convention, la documentation inclut des paramètres facultatifs comme id quand ils sont susceptibles d’apparaître dans une utilisation correcte.
La plupart des applications doivent choisir un schéma de routage de base et descriptif pour que les URL soient lisibles et explicites. La route conventionnelle par défaut {controller=Home}/{action=Index}/{id?} :
- Prend en charge un schéma de routage de base et descriptif.
- Est un point de départ pratique pour les applications basées sur une interface utilisateur.
- Est le seul modèle d’itinéraire nécessaire pour de nombreuses applications d’interface utilisateur web. Pour les applications d’interface utilisateur web plus volumineuses, une autre route utilisant Zones est souvent tout ce qui est nécessaire.
MapControllerRoute et MapAreaRoute :
- Attribuer automatiquement une valeur order à leurs terminaisons en fonction de l’ordre dans lequel elles sont invoquées.
Routage des points de terminaison dans ASP.NET Core 3.0 ou version ultérieure :
- N’a pas de concept de routes.
- Ne fournit pas de garanties d'ordre pour l’exécution de l’extensibilité. Tous les points de terminaison sont traités simultanément.
Activez la journalisation pour voir comment les implémentations de routage intégrées, comme Route, établissent des correspondances avec les requêtes.
Le routage des attributs est expliqué plus loin dans ce document.
Plusieurs routes conventionnelles
Plusieurs routes conventionnelles peuvent être ajoutées dans UseEndpoints en ajoutant d’autres appels à MapControllerRoute et MapAreaControllerRoute. Ceci permet de définir plusieurs conventions ou d’ajouter des routes conventionnelles qui sont dédiées à une action spécifique, comme :
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?}");
});
La routeblog dans le code précédent est une route conventionnelle dédiée. Il s’agit d’une route conventionnelle dédiée, car :
- Elle utilise un routage conventionnel.
- C’est dédié à une action spécifique.
Étant donné que le modèle de route "blog/{*article}" n’inclut pas controller et action en tant que paramètres :
- Ils peuvent uniquement avoir les valeurs
{ controller = "Blog", action = "Article" }par défaut . - Cette route correspond toujours à l’action
BlogController.Article.
/Blog, /Blog/Articleet /Blog/{any-string} sont les seuls chemins d’URL qui correspondent à la route du blog.
Dans l’exemple précédent :
- L’itinéraire
bloga une priorité plus élevée pour les correspondances que l’itinérairedefault, car vous l’ajoutez en premier. - Il s’agit d’un exemple de routage de style Slug où il est typique d’avoir un nom d’article dans le cadre de l’URL.
Warning
Dans ASP.NET Core version 3.0 ou ultérieure, le routage n'est pas :
- Définissez un concept appelé route.
UseRoutingajoute la mise en correspondance des routes au pipeline de middleware. L’UseRoutingintergiciel examine l’ensemble des points de terminaison définis dans l’application et sélectionne la meilleure correspondance des points de terminaison en fonction de la requête. - Fournissez des garanties sur l’ordre d’exécution de l’extensibilité, comme IRouteConstraint ou IActionConstraint.
Consultez Routage pour obtenir des informations de référence sur le routage.
Ordre de routage conventionnel
Le routage conventionnel correspond uniquement à une combinaison d’actions et de contrôleurs que l’application définit. Cette approche simplifie les cas où les itinéraires conventionnels se chevauchent.
Lorsque vous ajoutez des routes à l’aide de MapControllerRoute, MapDefaultControllerRoute et MapAreaControllerRoute, les points de terminaison reçoivent automatiquement une valeur d’ordre en fonction de l’ordre dans lequel vous appelez ces méthodes. Les correspondances à partir d’un itinéraire qui apparaît précédemment dans la liste ont une priorité plus élevée. Le routage conventionnel dépend de l’ordre. En général, placez des itinéraires avec des zones plus tôt, car ils sont plus spécifiques que les itinéraires sans zone.
Les routes conventionnelles dédiées avec des paramètres de route fourre-tout, tels que {*article}, peuvent rendre une route trop générique. Un itinéraire gourmand correspond aux URL que vous avez conçues pour être mises en correspondance par d’autres itinéraires. Mettez les routes gourmandes plus loin dans la table de routage pour éviter les correspondances gourmandes.
Warning
Un paramètre catch-all peut correspondre incorrectement aux itinéraires en raison d’un bogue de routage. Les applications affectées par ce bogue présentent les caractéristiques suivantes :
- Un routage catch-all, par exemple,
{**slug}" - Le routage catch-all ne correspond pas aux requêtes auxquelles il devrait correspondre.
- La suppression d’autres routages fait que le routage de type catch-all commence à fonctionner.
Consultez les bogues de GitHub 18677 et 16579 pour des cas d'exemple affectés par ce bogue.
Un correctif d’opt-in pour ce bogue est contenu dans .NET Core 3.1.301 ou version ultérieure sdk. Le code suivant définit un commutateur interne qui corrige ce bogue :
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Résoudre les actions ambiguës
Lorsque deux points de terminaison correspondent via le routage, le routage doit effectuer l’une des étapes suivantes :
- Choisissez le meilleur candidat.
- Levée d'une exception.
Par exemple:
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);
}
}
}
Le contrôleur précédent définit deux actions qui correspondent :
- Le chemin d’URL
/Products33/Edit/17 - Données de route
{ controller = Products33, action = Edit, id = 17 }.
Il s’agit d’un modèle classique pour les contrôleurs MVC :
-
Edit(int)affiche un formulaire pour modifier un produit. -
Edit(int, Product)traite le formulaire publié.
Pour déterminer l'itinéraire correct :
-
Edit(int, Product)est sélectionné lorsque la requête est un httpPOST. -
Edit(int)est sélectionné lorsque le verbe HTTP est autre chose.Edit(int)est généralement appelé par le biais deGET.
Le HttpPostAttribute, [HttpPost], est fourni pour le routage afin qu’il puisse choisir en fonction de la méthode HTTP de la requête. Le HttpPostAttribute rend Edit(int, Product) plus adapté que Edit(int).
Il est important de comprendre le rôle des attributs tels que HttpPostAttribute. Des attributs similaires sont définis pour d’autres verbes HTTP. Dans le routage conventionnel, il est courant que les actions utilisent le même nom d’action lorsqu’elles font partie d’un flux de travail de type montrer le formulaire, envoyer le formulaire. Par exemple, consultez Examiner les deux méthodes d’action Modifier.
Si le routage ne peut pas choisir le meilleur candidat, il génère une exception AmbiguousMatchException et répertorie les différents points de terminaison correspondants.
Noms de routes conventionnelles
Les chaînes "blog" et "default" dans les exemples suivants sont des noms de routes conventionnelles :
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?}");
});
Les noms de routes donnent à la route un nom logique. La route nommée peut être utilisée pour la génération d’URL. Utiliser une route nommée simplifie considérablement la création d’URL quand l’ordonnancement des routes peut rendre compliquée la génération des URL. Les noms de routes doivent être unique à l’échelle de l’application.
Noms d’itinéraires :
- N’ont aucun impact sur la correspondance d’URL ou la gestion des requêtes.
- Sont utilisés uniquement pour la génération d’URL.
Le concept de nom de route est représenté dans le routage sous la forme IEndpointNameMetadata. Les termes nom de la route et nom du point de terminaison :
- Sont interchangeables.
- Celui qui est utilisé dans la documentation et le code dépend de l’API décrite.
Routage d’attributs pour les API REST
Les API REST doivent utiliser le routage d’attributs pour modéliser les fonctionnalités de l’application sous la forme d’un ensemble de ressources dans lequel les opérations sont représentées par des verbes HTTP.
Le routage par attributs utilise un ensemble d’attributs pour mapper les actions directement aux modèles de routes. Le StartUp.Configurecode suivant est classique pour une REST API et est utilisé dans l’exemple suivant :
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();
});
}
Dans le code précédent, MapControllers est appelé à l’intérieur de UseEndpoints pour mapper les contrôleurs utilisant le routage par attributs.
Dans l’exemple suivant :
-
HomeControllercorrespond à un ensemble d’URL similaire à celui auquel la route conventionnelle par défaut{controller=Home}/{action=Index}/{id?}correspond.
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);
}
}
L’action HomeController.Index s’exécute pour l’un des chemins d’URL suivants : /, /Home, /Home/Index ou /Home/Index/3.
Cet exemple met en évidence une différence importante en termes de programmation entre le routage par attributs et le routage conventionnel. Le routage d’attributs nécessite plus d’entrée pour spécifier une route. La route par défaut conventionnelle gère les itinéraires de manière plus succincte. Cependant, le routage par attributs permet (et nécessite) un contrôle précis des modèles de routes qui s’appliquent à chaque action.
Avec le routage par attributs, les noms du contrôleur et de l’action n’entrent pas en ligne de compte dans la détermination de l’action mise en correspondance, sauf si le remplacement de jetons est utilisé. L’exemple suivant correspond aux mêmes URL que l’exemple précédent :
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);
}
}
Le code suivant utilise la substitution de jetons pour action et 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();
}
}
Le code suivant applique [Route("[controller]/[action]")] au contrôleur :
[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();
}
}
Dans le code précédent, les modèles de méthode Index doivent préfixer les modèles de route avec / ou ~/. Les modèles de routes appliqués à une action qui commencent par / ou ~/ ne sont pas combinés avec les modèles de routes appliqués au contrôleur.
Pour plus d’informations sur la sélection du modèle de route, consultez Priorité du modèle de route.
Noms de routage réservés
Les mots clés suivants sont des noms de paramètres de route réservés lorsque vous utilisez les contrôleurs ou les pages Razor :
actionareacontrollerhandlerpage
L’utilisation page comme paramètre d’itinéraire avec le routage d’attributs est une erreur courante. Ce choix entraîne un comportement incohérent et confus avec la génération d’URL.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
La génération d’URL utilise ces noms de paramètres spéciaux pour déterminer si une opération de génération d’URL fait référence à une Razor page ou à un contrôleur.
Les mots clés suivants sont réservés dans le contexte d’une Razor vue ou d’une Razor page :
pageusingnamespaceinjectsectioninheritsmodeladdTagHelperremoveTagHelper
N’utilisez pas ces mots clés pour les générations de liens, les paramètres liés au modèle ou les propriétés de niveau supérieur.
Modèles de verbes HTTP
ASP.NET Core inclut les modèles de verbe HTTP suivants :
Modèles de routage
ASP.NET Core inclut les modèles d’itinéraire suivants :
- Tous les modèles de verbe HTTP sont des modèles de routage.
- [Route]
Routage d’attributs avec des attributs de verbe HTTP
Examinons le contrôleur ci-dessous :
[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);
}
}
Dans le code précédent :
- Chaque action contient l’attribut
[HttpGet], qui limite la correspondance aux requêtes HTTP GET uniquement. - L’action
GetProductinclut le modèle"{id}", de sorte queidest ajouté au modèle"api/[controller]"dans le contrôleur. Le modèle de la méthode est"api/[controller]/{id}". Par conséquent, cette action correspond uniquement aux requêtes GET pour le formulaire/api/test2/xyz,/api/test2/123,/api/test2/{any string}et ainsi de suite.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); } - L’action
GetIntProductcontient le modèle"int/{id:int}". La portion:intdu modèle limite les valeurs de routeidaux chaînes pouvant être converties en un entier. Une requête GET vers/api/test2/int/abc:- Ne correspond pas à cette action.
- Retourne une erreur 404 Introuvable.
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- L’action
GetInt2Productcontient{id}dans le modèle, mais ne limiteidpas les valeurs qui peuvent être converties en entier. Une requête GET vers/api/test2/int2/abc:- Correspond à cette route.
- La liaison de modèle ne parvient pas à convertir
abcen nombre entier. Le paramètreidde la méthode est entier. - Retourne une 400 Requête incorrecte, car la liaison de modèle n’a pas pu convertir
abcen un entier.[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Le routage par attributs peut utiliser des attributs HttpMethodAttribute tels que HttpPostAttribute, HttpPutAttribute, et HttpDeleteAttribute. Tous les attributs de verbe HTTP acceptent un modèle de route. L’exemple suivant montre deux actions qui correspondent au même modèle de route :
[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);
}
}
À l’aide du chemin d’accès d’URL /products3 :
- L’action
MyProductsController.ListProductss’exécute lorsque le verbe HTTP estGET. - L’action
MyProductsController.CreateProducts’exécute lorsque le verbe HTTP estPOST.
Lors de la génération d’une REST API, il est rare que vous deviez utiliser [Route(...)] sur une méthode d’action, car l’action accepte toutes les méthodes HTTP. Il est préférable d’utiliser les attributs des verbes HTTP plus spécifiques pour plus de précision quant à ce qui est pris en charge par votre API. Les clients des REST API doivent normalement connaître les chemins et les verbes HTTP qui correspondent à des opérations logiques spécifiques.
REST API doivent utiliser le routage d’attributs pour modéliser les fonctionnalités de l’application sous la forme d’un ensemble de ressources dans lequel les opérations sont représentées par des verbes HTTP. Cette conception signifie que de nombreuses opérations, telles que GET et POST sur la même ressource logique, utilisent la même URL. Le routage d’attributs fournit le niveau de contrôle nécessaire pour concevoir soigneusement la disposition du point de terminaison public d’une API.
Dans la mesure où une route d’attribut s’applique à une action spécifique, il est facile de placer les paramètres nécessaires dans la définition du modèle de route. Dans l’exemple suivant, id est obligatoire dans le chemin d’accès d’URL :
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
L’action Products2ApiController.GetProduct(int) :
- S’exécute avec un chemin d’URL tel que
/products2/3 - Ne s’exécute pas avec le chemin d’URL
/products2.
L’attribut [Consumes] permet à une action de limiter les types de contenu de la demande pris en charge. Pour plus d’informations, consultez Définir les types de contenu de la requête pris en charge avec l’attribut [Consumes].
Consultez Routage pour obtenir une description complète des modèles de routes et des options associées.
Pour plus d’informations sur [ApiController], consultez Attribut ApiController.
Nom de l’itinéraire
Le code suivant définit un nom de route Products_List :
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Utilisez des noms de routage pour générer une URL basée sur un itinéraire spécifique. Noms d’itinéraires :
- N’ont aucun impact sur le comportement de correspondance d’URL du routage.
- Sont utilisés uniquement pour la génération d’URL.
Les noms de routes doivent être unique à l’échelle de l’application.
Comparez le code précédent avec la route par défaut conventionnelle, qui définit le paramètre id comme étant facultatif ({id?}). La possibilité de spécifier les API avec précision présente des avantages, par exemple de permettre de diriger /products et /products/5 vers des actions différentes.
Combiner des routes d’attributs
Pour rendre le routage des attributs moins répétitif, combinez les attributs de routage sur le contrôleur avec des attributs de routage sur les actions individuelles. Les modèles de route que vous définissez sur le contrôleur sont ajoutés en préfixe aux modèles de route des actions. Lorsque vous placez un attribut de routage sur le contrôleur, toutes les actions du contrôleur utilisent le routage des attributs.
[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);
}
}
Dans l’exemple précédent :
- Le chemin d’URL
/productspeut correspondre àProductsApi.ListProducts - Le chemin d’accès de l’URL
/products/5peut correspondre àProductsApi.GetProduct(int).
Ces deux actions correspondent seulement à HTTP GET, car elles sont marquées avec l’attribut [HttpGet].
Modèles de routage que vous appliquez à une action et qui commencent par / ou ~/ ne sont pas combinés avec les modèles de routage que vous appliquez au contrôleur. L'exemple suivant correspond à un ensemble de chemins d'accès URL similaires à la route par défaut.
[Route("Home")]
public class HomeController : Controller
{
[Route("")]
[Route("Index")]
[Route("/")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
[Route("About")]
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Le tableau suivant décrit les [Route] attributs dans le code précédent.
| Attribute | Combine avec [Route("Home")] |
Définit le modèle de route |
|---|---|---|
[Route("")] |
Yes | "Home" |
[Route("Index")] |
Yes | "Home/Index" |
[Route("/")] |
Non | "" |
[Route("About")] |
Yes | "Home/About" |
Ordre de route des attributs
Le routage génère une arborescence et fait correspondre simultanément tous les points de terminaison.
- Les entrées d’itinéraire se comportent comme si elles sont placées dans un ordre idéal.
- Les routes les plus spécifiques sont susceptibles d'être exécutées avant les routes plus générales.
Par exemple, une route d’attribut comme blog/search/{topic} est plus spécifique qu’une route d’attribut comme blog/{*article}. La route blog/search/{topic} a une priorité plus élevée, par défaut, car elle est plus spécifique. Avec le routage conventionnel, le développeur est responsable du placement des routes dans l’ordre souhaité.
Les routes d’attributs peuvent configurer un ordre à l’aide de la propriété Order. Tous les attributs de route fournis par le framework incluent Order . Les routes sont traitées selon un ordre croissant de la propriété Order. L’ordre par défaut est 0. La définition d’une route avec Order = -1 fait que cette route s’exécute avant les routes qui ne définissent pas d’ordre. Définir une route à l’aide de Order = 1 s’effectue après l’application de l’ordre de routage par défaut.
Évitez de dépendre de Order. Si l’espace d’URL d’une application nécessite des valeurs d’ordre explicites pour router correctement, il est probable qu’il prête également à confusion pour les clients. D’une façon générale, le routage par attributs sélectionne la route correcte avec la mise en correspondance d’URL. Si l’ordre par défaut utilisé pour la génération d’URL ne fonctionne pas, l’utilisation à titre de remplacement d’un nom de route est généralement plus simple que d’appliquer la propriété Order.
Considérez les deux contrôleurs suivants qui définissent tous deux la route correspondant à /home :
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);
}
}
Le fait de demander /home à l’aide du code précédent lève une exception semblable à la suivante :
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
L’ajout Order à l’un des attributs de route résout l’ambiguïté :
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
Avec le code précédent, /home exécute le point de terminaison HomeController.Index. Pour accéder à MyDemoController.MyIndex, demandez /home/MyIndex.
Remarque :
- Le code précédent est un exemple de conception de routage médiocre. Il présente la propriété
Order. - La
Orderpropriété résout uniquement l’ambiguïté. Ce modèle ne peut pas être mis en correspondance. Il est préférable de supprimer le[Route("Home")]modèle.
Pour plus d’informations sur l’ordre des routes avec Razor Pages, consultez Razor les conventions de routage et d’application de Pages : Ordre des routes.
Dans certains cas, une erreur HTTP 500 est retournée avec des routes ambiguës. Utilisez la journalisation pour voir quels points de terminaison sont à l’origine de AmbiguousMatchException.
Remplacement de jetons dans les modèles de route [contrôleur], [action], [zone]
Pour plus de commodité, les routes définies par attribut prennent en charge leremplacement de jetons, en entourant un jeton de crochets ([, ]). Les jetons [action], [area]et [controller] sont remplacés par les valeurs du nom de l’action, du nom de la zone et du nom du contrôleur à partir de l’action où vous définissez l’itinéraire :
[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);
}
}
Dans le code précédent :
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Correspondances
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Correspondances
/Products0/Edit/{id}
Le remplacement des jetons a lieu lors de la dernière étape de la création des routes définies par attributs. L’exemple précédent se comporte de la même manière que le code suivant :
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);
}
}
Si vous lisez cela dans une langue autre que l'anglais, faites-le nous savoir dans cette GitHub problème de discussion si vous souhaitez voir les commentaires de code dans votre langue native.
Vous pouvez également combiner des itinéraires d’attributs avec l’héritage. Cette combinaison est puissante lorsque vous utilisez le remplacement de symboles. Le remplacement des jetons s’applique aussi aux noms de routes définis par des routes d’attribut.
[Route("[controller]/[action]", Name="[controller]_[action]")] génère un nom de route unique pour chaque action :
[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);
}
}
Pour faire correspondre littéralement au délimiteur de remplacement de jeton [ ou ], placez-le en échappement en répétant le caractère ([[ ou ]]).
Utiliser un transformateur de paramètre pour personnaliser le remplacement des jetons
Vous pouvez personnaliser le remplacement de jeton à l’aide d’un transformateur de paramètres. Un transformateur de paramètre implémente IOutboundParameterTransformer et transforme la valeur des paramètres. Par exemple, un transformateur de paramètre SlugifyParameterTransformer personnalisé transforme la valeur de la route SubscriptionManagement en 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();
}
}
RouteTokenTransformerConvention est une convention de modèle d’application qui :
- Appliquez un transformateur de paramètres à tous les itinéraires d’attribut dans une application.
- Personnalise les valeurs des jetons des routes définies par attributs lors de leur remplacement.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
La méthode ListAllprécédente correspond à /subscription-management/list-all.
RouteTokenTransformerConvention est inscrit en tant qu’option dans ConfigureServices.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
}
Pour obtenir la définition de slug, consultez la documentation web MDN sur Slug.
Warning
Lorsque vous utilisez System.Text.RegularExpressions pour traiter une entrée non approuvée, passez un délai d’expiration. Un utilisateur malveillant peut fournir une entrée à RegularExpressions, provoquant une attaque par déni de service. Les API d’infrastructure ASP.NET Core qui utilisent RegularExpressions passent un délai d’expiration.
Routes à attributs multiples
Le routage par attributs prend en charge la définition de plusieurs routes pour atteindre la même action. L’utilisation la plus courante de ceci est d’imiter le comportement de la route conventionnelle par défaut, comme le montre l’exemple suivant :
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
Le fait de placer plusieurs attributs de route sur le contrôleur signifie que chacun d’eux se combine avec chacun des attributs de route sur les méthodes d’action :
[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();
}
}
Toutes les contraintes de routes de verbe HTTP implémentent IActionConstraint.
Lorsque plusieurs attributs de route qui implémentent IActionConstraint sont placés sur une action :
- Chaque contrainte d’action se combine avec le modèle de route appliqué au contrôleur.
[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();
}
}
L’utilisation de plusieurs routes sur des actions peut sembler utile et puissante. Il est préférable de conserver l’espace URL de votre application de base et bien défini. Utilisez plusieurs routes sur les actions seulement là où c’est nécessaire, par exemple pour prendre en charge des clients existants.
Spécification facultative de paramètres, de valeurs par défaut et de contraintes pour les routes d’attribut
Les routes d’attribut prennent en charge la même syntaxe inline que les routes conventionnelles pour spécifier des paramètres, des valeurs par défaut et des contraintes facultatifs.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Dans le code précédent, [HttpPost("product14/{id:int}")] applique une contrainte de route. L’action Products14Controller.ShowProduct est mise en correspondance uniquement par des chemins d’accès d’URL tels que /product14/3. La partie {id:int} modèle de routage limite ce segment à des entiers uniquement.
Pour obtenir une description détaillée de la syntaxe du modèle de routage, consultez Référence du modèle de routage.
Attributs de route personnalisés à l’aide de IRouteTemplateProvider
Tous les attributs de route implémentent IRouteTemplateProvider. L'environnement d'exécution ASP.NET Core :
- Recherche des attributs sur les classes de contrôleur et les méthodes d’action au démarrage de l’application.
- Utilise les attributs qui implémentent
IRouteTemplateProviderpour générer l’ensemble initial de routes.
Implémentez IRouteTemplateProvider pour définir des attributs de route personnalisés. Chaque IRouteTemplateProvider vous permet de définir une route avec un modèle, un nom et un ordre de route personnalisés :
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();
}
}
La méthode précédente Get retourne Order = 2, Template = api/MyTestApi.
Utiliser le modèle d’application pour personnaliser les routes des attributs
Le modèle d'application
- est un modèle objet créé au démarrage.
- Contient toutes les métadonnées utilisées par ASP.NET Core pour acheminer et exécuter les actions dans une application.
Le modèle d’application inclut toutes les données collectées à partir des attributs de route. Les données des attributs de routage sont fournies par l’implémentation IRouteTemplateProvider. Conventions:
- Peut être écrit pour modifier le modèle d’application afin de personnaliser le comportement du routage.
- Sont lus au démarrage de l’application.
Cette section présente un exemple de base de personnalisation du routage à l’aide du modèle d’application. Le code suivant aligne approximativement les routes avec la structure de dossiers du projet.
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()
};
}
}
}
Le code suivant empêche l’application de la namespace convention aux contrôleurs qui sont routés par l’attribut :
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Par exemple, le contrôleur suivant n’utilise pas 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}");
}
}
La méthode NamespaceRoutingConvention.Apply :
- Ne fait rien si le contrôleur utilise le routage par attributs.
- Définit le modèle de contrôleurs en fonction de
namespace, avec la basenamespacesupprimée.
Le NamespaceRoutingConvention peut être appliqué dans 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.
Par exemple, prenons le contrôleur suivant :
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}");
}
}
}
Dans le code précédent :
- La base
namespaceestMy.Application. - Le nom complet du contrôleur précédent est
My.Application.Admin.Controllers.UsersController. - Le
NamespaceRoutingConventiondéfinit le modèle de contrôleurs surAdmin/Controllers/Users/[action]/{id?.
Le NamespaceRoutingConvention peut également être appliqué en tant qu’attribut sur un contrôleur :
[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}");
}
}
Routage mixte : routage conventionnel et routage par attributs
ASP.NET Core applications peuvent combiner l’utilisation du routage conventionnel et du routage d’attributs. En général, vous utilisez le routage conventionnel pour les contrôleurs qui renvoient des pages HTML aux navigateurs, et le routage par attribut pour les contrôleurs qui exposent des API REST.
Les actions sont routées de façon conventionnelle ou routées par attribut. Le fait de placer une route sur le contrôleur ou sur l’action les rend « routés par attribut ». Les actions qui définissent des routes d’attribut ne sont pas accessibles via les routes conventionnelles et vice versa. Toute action acheminée par l’attribut all du contrôleur.
Le routage d’attributs et le routage conventionnel utilisent le même moteur de routage.
Génération d’URL et valeurs ambiantes
Les applications peuvent utiliser les fonctionnalités de génération d’URL de routage pour générer des liens URL vers des actions. La génération d’URL élimine le codage en dur des URL, ce qui rend un code plus robuste et plus facile à maintenir. Cette section se concentre sur les fonctionnalités de génération d’URL fournies par MVC et couvre seulement les principes de base du fonctionnement de la génération d’URL. Pour une description détaillée de la génération d’URL, consultez Routage.
L’interface IUrlHelper est l’élément d’infrastructure sous-jacent entre MVC et le routage pour la génération d’URL. Une instance de IUrlHelper est disponible par le biais de la propriété Url dans les contrôleurs, les vues et les composants de vue.
Dans l’exemple suivant, l’interface IUrlHelper est utilisée par le biais de la propriété Controller.Url pour générer une URL vers une autre action.
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();
}
}
Si l’application utilise l’itinéraire classique par défaut, la valeur de la url variable est la chaîne /UrlGeneration/Destinationde chemin d’URL. Le routage crée ce chemin d’URL en combinant :
- Valeurs de routage de la requête actuelle, appelées valeurs ambiantes.
- Valeurs transmises à
Url.Actionet en remplaçant ces valeurs dans le modèle de route :
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
Dans le modèle de route, chaque paramètre de route a sa valeur remplacée en faisant correspondre les noms aux valeurs et aux valeurs ambiantes. Un paramètre de route qui n’a pas de valeur peut :
- Utilisez une valeur par défaut s’il en a une.
- Peut être ignoré si c'est facultatif. Par exemple, le
idissu du modèle de route{controller}/{action}/{id?}.
La génération d’URL échoue si un paramètre de route obligatoire n’a pas de valeur correspondante. Si la génération d’URL échoue pour une route, la route suivante est essayée, ceci jusqu’à ce que toutes les routes aient été essayées ou qu’une correspondance soit trouvée.
L’exemple précédent de Url.Action suppose le routage conventionnel. La génération d’URL fonctionne de façon similaire avec le routage par attributs, même si les concepts sont différents. Avec un routage conventionnel :
- Les valeurs de route sont utilisées pour développer un modèle.
- Les valeurs de routage pour
controlleretactions’affichent généralement dans ce modèle. Cela fonctionne, car les URL mises en correspondance par le routage respectent une convention.
L’exemple suivant utilise un attribut de routage :
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();
}
}
L’action Source dans le code précédent génère custom/url/to/destination.
LinkGenerator a été ajouté dans ASP.NET Core 3.0 comme alternative à IUrlHelper.
LinkGenerator offre des fonctionnalités similaires, mais plus flexibles. Chaque méthode sur IUrlHelper a également une famille de méthodes correspondante sur LinkGenerator.
Génération des URL par nom d’action
Url.Action, LinkGenerator.GetPathByAction et toutes les surcharges associées sont tous conçus pour générer le point de terminaison cible en spécifiant un nom de contrôleur et un nom d’action.
Lorsque vous utilisez Url.Action, le runtime fournit les valeurs d’itinéraire actuelles pour controller et action:
- Les valeurs de
controlleret deactionfont partie à la fois des valeurs ambiantes et des valeurs. La méthodeUrl.Actionutilise toujours les valeurs actuelles deactionet decontroller, et génère un chemin d’accès d’URL qui route vers l’action actuelle.
Le routage essaye d’utiliser les valeurs dans les valeurs ambiantes pour renseigner les informations qui n’ont pas été fournies lors de la génération d’une URL. Considérez une route comme {a}/{b}/{c}/{d} avec des valeurs ambiantes { a = Alice, b = Bob, c = Carol, d = David } :
- Le routage contient suffisamment d’informations pour générer une URL sans valeurs supplémentaires.
- Le routage contient suffisamment d’informations, car tous les paramètres de route ont une valeur.
Si la valeur { d = Donovan } est ajoutée :
- La valeur
{ d = David }est ignorée. - Le chemin d’accès d’URL généré est
Alice/Bob/Carol/Donovan.
Avertissement : Les chemins d’accès d’URL sont hiérarchiques. Dans l’exemple précédent, si la valeur { c = Cheryl } est ajoutée :
- Les deux valeurs
{ c = Carol, d = David }sont ignorées. - Il n’y a plus de valeur pour
det la génération d’URL échoue. - Vous devez spécifier les valeurs souhaitées de
cet dedpour générer une URL.
Vous pouvez vous attendre à rencontrer ce problème avec l’itinéraire {controller}/{action}/{id?}par défaut. Ce problème est rare dans la pratique, car Url.Action spécifie toujours explicitement une controller valeur et action.
Plusieurs surcharges de Url.Action prennent un objet de valeurs d’itinéraire pour fournir des valeurs pour les paramètres de route autres que controller et action. L’objet valeurs de route est fréquemment utilisé avec id. Par exemple : Url.Action("Buy", "Products", new { id = 17 }). L'objet des valeurs de route
- Par convention est généralement un objet de type anonyme.
- Il peut s’agir d’un
IDictionary<>ou d’un POCO).
Toutes les valeurs d’itinéraire supplémentaires qui ne correspondent pas aux paramètres d’itinéraire vont dans la chaîne de requête.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url);
}
Le code précédent génère /Products/Buy/17?color=red.
Le code suivant génère une URL absolue :
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);
}
Pour créer une URL absolue, utilisez l’une des options suivantes :
- Une surcharge qui accepte un
protocol. Par exemple, le code précédent. - LinkGenerator.GetUriByAction, qui génère des URI absolus par défaut.
Générer des URL par route
Le code précédent a montré la génération d’une URL en passant le nom du contrôleur et le nom de l’action.
IUrlHelper fournit également la famille de méthodes Url.RouteUrl. Ces méthodes sont similaires à Url.Action, mais elle ne copient pas les valeurs actuelles de action et de controller vers les valeurs de route. Utilisation la plus courante pour Url.RouteUrl :
- Spécifie un nom d’itinéraire pour générer l’URL.
- En règle générale, ne spécifie pas de contrôleur ou de nom d’action.
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();
}
Le fichier suivant Razor génère un lien HTML versDestination_Route :
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Générer des URL en HTML et Razor
IHtmlHelper fournit les HtmlHelperméthodes Html.BeginForm et Html.ActionLink pour générer <form> et <a> éléments respectivement. Ces méthodes utilisent la méthode Url.Action pour générer une URL et ils acceptent les arguments similaires. Les pendants de Url.RouteUrl pour HtmlHelper sont Html.BeginRouteForm et Html.RouteLink, qui ont des fonctionnalités similaires.
Les TagHelpers génèrent des URL via le TagHelper form et le TagHelper <a>. Ils utilisent tous les deux IUrlHelper pour leur implémentation. Pour plus d’informations, consultez Tag Helpers dans les formulaires.
Dans les vues, la IUrlHelper propriété est disponible via la Url propriété pour toute génération d’URL ad hoc non couverte par les méthodes précédentes.
Génération d’URL dans les résultats d'action
Les exemples précédents montrent comment utiliser IUrlHelper dans un contrôleur. L’utilisation la plus courante dans un contrôleur consiste à générer une URL dans le cadre du résultat d’une action.
Les classes de base ControllerBase et Controller fournissent des méthodes pratiques pour les résultats d’action qui référencent une autre action. Une utilisation typique est de rediriger après acceptation de l’entrée utilisateur :
[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);
}
Les méthodes de fabrique de résultats d’action comme RedirectToAction et CreatedAtAction suivent un modèle similaire aux méthodes sur IUrlHelper.
Cas spécial pour les routes conventionnelles dédiées
Le routage conventionnel peut utiliser un type spécial de définition de route appelé route conventionnelle dédiée. Dans l’exemple suivant, la route nommée blog est une route conventionnelle dédiée :
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?}");
});
À l’aide des définitions de routage précédentes, Url.Action("Index", "Home") génère le chemin / d’URL à l’aide de l’itinéraire default , mais pourquoi ? Vous pourriez supposer que les valeurs de route { controller = Home, action = Index } suffisent pour générer une URL en utilisant blog, et le résultat serait /blog?action=Index&controller=Home.
Les routes conventionnelles dédiées s’appuient sur un comportement spécial des valeurs par défaut qui n’ont pas de paramètre de route correspondant, ce qui empêche la route d’être trop gourmande lors de la génération d’URL. Dans ce cas, les valeurs par défaut sont { controller = Blog, action = Article }, et ni controller ni action n’apparaissent comme paramètre de route. Quand le routage effectue une génération d’URL, les valeurs fournies doivent correspondre aux valeurs par défaut. La génération d’URL avec blog échoue, car les valeurs { controller = Home, action = Index } ne correspondent pas à { controller = Blog, action = Article }. Le routage essaye alors d’utiliser default, ce qui réussit.
Areas
Les zones sont une fonctionnalité MVC utilisée pour organiser des fonctionnalités connexes dans un groupe distinct :
- Espace de noms de routage pour les actions du contrôleur.
- Structure des dossiers pour les vues.
L’utilisation de zones permet à une application d’avoir plusieurs contrôleurs portant le même nom, pour autant qu’ils soient dans des zones différentes. L’utilisation de zones crée une hiérarchie qui permet le routage par ajout d’un autre paramètre de route, area, à controller et à action. Cette section explique comment le routage interagit avec les zones. Consultez Zones pour savoir comment les zones sont utilisées avec les vues.
L’exemple suivant configure MVC pour utiliser la route conventionnelle par défaut et une route area pour un area nommé Blog :
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
Dans le code précédent, MapAreaControllerRoute est appelé pour créer le "blog_route". Le deuxième paramètre, "Blog", est le nom de la zone.
Lors de la correspondance d’un chemin d’URL comme /Manage/Users/AddUser, la route "blog_route" génère les valeurs de route { area = Blog, controller = Users, action = AddUser }. La area valeur de route provient d’une valeur par défaut pour area. L’itinéraire créé par MapAreaControllerRoute est équivalent au code suivant :
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 crée un itinéraire en utilisant à la fois une valeur par défaut et une contrainte pour area, à l’aide du nom de zone fourni, dans ce cas Blog. La valeur par défaut garantit que l’itinéraire produit toujours { area = Blog, ... }, et la contrainte impose la valeur { area = Blog, ... } pour la génération d’URL.
Le routage conventionnel est dépendant de l’ordre. En général, placez les itinéraires avec des zones plus tôt, car ils sont plus spécifiques que les itinéraires sans zone.
Avec l’exemple précédent, les valeurs de route { area = Blog, controller = Users, action = AddUser } sont mises en correspondance avec l’action suivante :
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}");
}
}
}
L’attribut [Zone] indique qu’un contrôleur fait partie d’une zone. Ce contrôleur se trouve dans la zone Blog. Les contrôleurs sans l’attribut [Area] n’appartiennent à aucune zone et ne correspondent pas lorsque la valeur de route area est fournie par le mécanisme de routage. Dans l’exemple suivant, seul le premier contrôleur répertorié peut correspondre aux valeurs de route { area = Blog, controller = Users, action = AddUser }.
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace1
{
[Area("Blog")]
public class UsersController : Controller
{
// GET /manage/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace2
{
// Matches { area = Zebra, controller = Users, action = AddUser }
[Area("Zebra")]
public class UsersController : Controller
{
// GET /zebra/users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Namespace3
{
// Matches { area = string.Empty, controller = Users, action = AddUser }
// Matches { area = null, controller = Users, action = AddUser }
// Matches { controller = Users, action = AddUser }
public class UsersController : Controller
{
// GET /users/adduser
public IActionResult AddUser()
{
var area = ControllerContext.ActionDescriptor.RouteValues["area"];
var actionName = ControllerContext.ActionDescriptor.ActionName;
var controllerName = ControllerContext.ActionDescriptor.ControllerName;
return Content($"area name:{area}" +
$" controller:{controllerName} action name: {actionName}");
}
}
}
Pour être exhaustif, l’espace de noms de chaque contrôleur est affiché ici. Si les contrôleurs précédents utilisaient le même espace de noms, une erreur du compilateur serait générée. Les espaces de noms de classe n’ont pas d’effet sur le routage de MVC.
Les deux premiers contrôleurs appartiennent à des zones et ne correspondent que lorsque le nom de leur zone respective est fourni par la valeur d’itinéraire area. Le troisième contrôleur n’est membre d’aucune zone et peut être trouvé en correspondance seulement quand aucune valeur pour area n’est fournie par le routage.
En termes de mise en correspondance avec aucune valeur, l’absence de la valeur area est identique à une valeur null ou de chaîne vide pour area.
Lors de l’exécution d’une action à l’intérieur d’une zone, la valeur de route pour area est disponible en tant que valeur ambiante, que le routage peut utiliser pour la génération d’URL. Cela signifie que, par défaut, les zones sont conservées lors de la génération d’URL, comme le montre l’exemple suivant.
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);
}
}
}
Le code suivant génère une URL vers /Zebra/Users/AddUser :
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Définition d’action
Les méthodes publiques sur un contrôleur, sauf celles qui sont avec l’attribut NonAction, sont des actions.
Exemple de code
- MyDisplayRouteInfo est fourni par le package NuGet Rick.Docs.Samples.RouteInfo et affiche les informations de routage.
- Afficher ou télécharger un exemple de code (comment télécharger)
Déboguer les diagnostics
Pour obtenir une sortie de diagnostic de routage détaillée, définissez Logging:LogLevel:Microsoft sur Debug. Dans l'environnement Development, définissez le niveau de journalisation dans appsettings.Development.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}