ASP.NET MVC Trucs et astuces - Gestion des routes

Les vacances sont terminées pour moi ! La rentrée est l’occasion de démarrer une série d’articles que je souhaite faire depuis longtemps sur ASP.NET MVC. Il s’agit de détailler les points qui vous serviront au quotidien en temps que développeur ASP.NET MVC. Il n’y a pas de liste fixée, je ferai celà en fonction de mon humeur et des projets sur lesquels je travaille Smile Clairement, je compte reprendre les basiques de la techno, cette série s’adressera donc en grande partie aux personnes qui ne connaissent pas ou que peu ASP.NET MVC.

Pour commencer la série, je vous propose de nous intéresser aux routes, qui sont au coeur même du fonctionnement du Framework MVC d’ASP.NET.

Qu’est-ce qu’une route MVC ?

Une route MVC est la définition d’un pattern permettant d’interpréter une URL et d’en extraire les informations nécessaires à l’instanciation d’un contrôleur, l’exécution d’une action et le passage de paramètre (ou non) à cette action.

Lorsque vous créez un projet ASP.NET MVC, une première route est automatiquement définie : il s’agit de la route par défaut. Pour visualiser sa définition, rendez-vous dans le Global.asax ou dans le fichier RouteConfig.cs du dossier App_Start, selon la version de MVC avec laquelle vous développez :

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Comme il est possible de le constater, une route MVC est caractérisée à minima par trois propriétés :

  • un nom, unique au sein de la table de routes
  • une URL, ou plus génériquement un pattern d’URL
  • des valeurs par défaut si certains éléments ne sont pas précisés dans l’URL

Ici, la route est définie de manière totalement générique selon le pattern Controller/Action/Id, ce qui signifie que n’importe quelle URL composée de trois segments sera interprétée de sorte que :

  • Le premier segment représente le nom du contrôleur
  • Le second segment représente le nom de l’action à exécuter
  • Le troisième segment (s’il existe) représente un paramètre nommé “id” à passer à l’action

Ainsi, les URLs suivantes seront interprétées de la sorte par votre application :

  • /Customer/Edit/32 : tentative de résolution d’un contrôleur nommé “CustomerController”, appel de la méthode Edit prenant en paramètre une variable “id” ayant pour valeur 32.
  • /Customer : tentative de résolution d’un contrôleur nommé “CustomerController”, appel de la méthode Index, par défaut
  • / : tentative de résolution d’un contrôleur nommé “HomeController”, appel d’une méthode Index
  • /Customer/Edit/Corioland : tentative de résolution d’un contrôleur nommé “CustomerController”, appel de la méthode Edit prenant en paramètre une variable “id” ayant pour valeur Corioland

Comme le montrent les différents exemples ci-dessus, aucune contraite de type n’est portée sur le paramètre id, qui peut être aussi bien un entier, qu’un chaîne de caractère ou autre…

D’ailleurs, ce paramètre ne s’appel id que parce qu’il a été défini tel quel dans le pattern de la route par défaut. Si vous modifiez cette route comme suivant “{controller}/{action}/{nom}” alors vos actions devront avoir un paramètre nommé “nom” et non “id”.

Définir ses propres routes

Une bonne pratique du développement ASP.NET MVC consiste à créer ses propres routes, tout d’abord pour ne pas utiliser la route par défaut que n’importe qui peut deviner (/Admin/User/1, facile) mais aussi pour faciliter la lecture de l’URL pour l’utilisateur ou l’indexation de celle-ci par les moteurs de recherche.

Il est possible d’écrire des routes moins génériques que celle par défaut, en forçant par exemple un contrôleur ou une action. Par exemple, sur ce blog, vous pouvez constater que l’url de ce billet est formée de la sorte : “/b/julien/Archives/lien-du-billet”. Dans ce cas, le pattern de la route est le suivant :

routes.MapRoute(
    name: "Default_Blog",
    url: "b/{blog}/archives/{permalink}",
    defaults: new { controller = "Blog", action = "Post" }
);

On appelle donc un contrôleur “BlogController” et une action “Post” qui va prendre en paramètre le répertoire virtuel du blog et le permalink du post à afficher en paramètres!

Lorsque IIS reçoit une requête HTTP, il transfert celle-ci au moteur ASP.NET qui instancie le moteur de route MVC pour la résoudre (et déterminer le contrôleur, l’action etc…). Le moteur de routes MVC utilise la table de route, dans laquelle sont enregistrées les routes au démarrage de l’application. Aussi, il est très important de faire attention à enregistrer les routes de la plus spécifique à la plus générique, afin que celles-ci ne se télescopent pas les unes les autres. Par exemple, si la route par défaut est enregistrées en premier, alors elle sera utilisée pour quasiment toutes les requêtes HTTP et les autres seront ignorées.

Contraindre une route

Comme je le disais plus haut dans l’article, les paramètres qui peuvent être pris par les actions des contrôleurs n’ont par défaut qu’une seule contrainte : leur nom. Il est par contre possible d’ajouter un certain nombre de contraintes pour limiter les possibilités, par exemple de forcer un type numérique pour les ids. Pour celà, il suffit d’utiliser une expression régulière dans la définition du pattern de la route :

routes.MapRoute(
    name: "Client",
    url: "Client/{action}/{id}",
    defaults: new { controller = "Client", action = "Index", id = UrlParameter.Optional },
    constraints:new { id = @"\d+" }
);

Cette définition de route contraint donc le paramètre id à être numérique. Si ce n’est pas le cas, la route est ignorée et reste non résolue (sauf si une route enregistrée plus tard dans la table de routes correspond, évidemment).

Vous pouvez aussi imposer l’utilisation d’un verbe Http pour une route données, en définissant une instance de HttpMethodConstraint sur le type anonyme qui définit vos contraintes :

routes.MapRoute(
    name: "Client",
    url: "Client/{action}/{id}",
    defaults: new { controller = "Client", action = "Index", id = UrlParameter.Optional },
    constraints:new { id = @"\d+", method = new HttpMethodConstraint("GET") }
);

Définir ses propres contraintes de routes

Il est possible d’aller beaucoup plus loin dans l’utilisation de routes contraintes et de créer ses propres contraintes de route. Pour cela, il suffit d’implémenter l’interface IRouteConstraint comme suivant :

public class NotAnonymousRouteConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, 
        RouteValueDictionary values, RouteDirection routeDirection)
    {
        return httpContext.User.Identity.IsAuthenticated;
    }
}

Puis de déclarer une propriété de ce type dans le type anonyme qui représente vos contraintes à l’enregistrement de la route (le nom n’a pas d’importance ici) :

routes.MapRoute(
    name: "Client",
    url: "Client/{action}/{id}",
    defaults: new { controller = "Client", action = "Index", id = UrlParameter.Optional },
    constraints: new { id = @"\d+", anonymous = new NotAnonymousRouteConstraint() }
);

Cela offre clairement des possibilités énormes en terme de routing ! Attention cependant à éviter des opérations coûteuses à ce niveau, par exemple se connecter à une base de données pour valider qu’un id existe en base : ce cas doit être traité dans l’action qui peut être facilement mise en cache à l’aide d’un output cache Winking smile

Utilisation des attributs de routage

Au delà de la définition pure et simple des patterns de routes, il est possible d’utiliser des attributs de routage directement sur les actions d’un contrôleur afin d’orienter le moteur de résolution de route sur la route à utiliser. Pour cela, ASP.NET MVC défini les attributs suivants :

  • AcceptVerb, HttpPost, HttpGet, HttpPut, HttpDelete
  • ActionName

Les premiers permettent d’indiquer que si jamais deux méthodes d’un même contrôleur pourrait être valide pour une même URL lors de la résolution de la route, le verbe http utilisé pour la requête doit être discriminent. En ASP.NET MVC, on utilise principalement HttpGet pour renvoyer la vue et HttpPost pour soumettre le formulaire de la vue (les actions auront alors le même nom). On utilise plus facilement les autres verbes lorsque l’on travaille sur des services Web API, par exemple.

Le second, ActionName, permet de spécifier un nom à votre action, sans pour autant que la méthode du contrôleur possède ce nom.

Voilà pour ce premier article de “trucs et astuces” autour d’ASP.NET MVC.

A bientôt pour la suite

Julien Winking smile

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus