Archives Wild Code School
ASP.Net

ASP.Net: Routage

Quête 5 sur 7

ASP.Net: Routage


Objectifs

  • Comprendre le principe d'itinéraire
  • Router une action d'un contrôleur par convention
  • Router une action d'un contrôleur par attribut

Le routage

Lorsque tu prends le train, tu achètes un billet qui t'amène d'un endroit A à un endroit B. Afin d'atteindre le train, tu dois suivre un certain itinéraire dans la gare pour atteindre le quai auquel est garé le train. Pour pouvoir atteindre le bon quai, tu regardes un des écrans mis à disposition pour pouvoir connaître la route à emprunter. Aussi, tu peux éventuellement demander au guichet en lui fournissant des informations telles que:

  • Ta destination
  • L'heure de départ
  • La classe (première ou seconde)

En fonction des informations que tu lui fournis, le guichetier t'indique un itinéraire. Dans cette situation, le guichetier est en quelque sorte ton routeur: il t'indique le chemin à suivre pour atteindre la destination que tu désires.

En ASP.Net, le routeur s'occupe d'acheminer les requêtes HTTP en fonction de l'URL et de paramètres de celle-ci à une action d'un contrôleur.

↗
Routing

Documentation officielle sur le routing en ASP.Net

learn.microsoft.com

Maintenant que tu sais en quoi consiste le routage, comment le met-on en place en ASP.Net ?

Configuration

Ajouter à la queue

Le routage en ASP.Net se place dans la queue intergicielle. Il y a de particulier qu'une paire de deux intergiciels est nécessaire. L'emplacement de ceux-ci dans la queue intergicielle est crucial.

Voici les deux intergiciels en jeu dans le routage:

  • UseRouting - Examine tous les points de terminaison (routes) disponibles et redirige la requête au bon point
  • UseEndpoints - Fait correspondre aux points de terminaison des fonctions déléguées

Program.cs

csharp
public static void Main(string[] args)
{
    [...]

    // Configure the HTTP request pipeline.
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

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

    app.UseRouting(); // Routing should be placed here in the pipeline
    
    app.UseAuthorization();
    

    // UseEndpoints should be the last in the pipeline
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

Points de terminaison

Un point de terminaison est la destination d'un itinéraire: c'est la route définie qui fournira la réponse. Si aucun point de terminaison n'est trouvé, ASP.Net envoie une erreur 404 - Not Found.

Il existe une multitude de manières de configurer des points de terminaison d'une application ASP.Net.

Itinéraire conventionnel

Par défaut, dans un projet ASP.Net basique généré par Visual Studio 2019, le routage par convention est configuré. Selon le type de projet le routage conventionnel peut-être configuré différement. Nous allons voir la méthode la plus compréhensible et claire:

/Program.cs

csharp
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // [...] adding other middlewares to the pipeline

    app.UseRouting();

    // [...] adding other middlewares to the pipeline

    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
}
  • app.MapControllerRoute - Configure un itinéraire conventionnel par défaut vers des contrôleurs (nous verrons les détails de cet itinéraire un peu plus bas).

Qu'est-ce que fait exactement la méthode MapControllerRoute ?

Lorsqu'est utilisé ASP.Net MVC, chaque action réalisable est une méthode d'un contrôleur. Cette méthode est associée à un point de terminaison (une route/URI). L'association conventionnelle veut que soit cartographiée chaque route d'un contrôleur en fonction de ... conventions !

Un petit exemple pour mieux comprendre:

/Controllers/HelloController.cs

csharp
public class HelloController
{
    public String SayHello()
    {
        return "Hello !";
    }

    public String YellHello()
    {
        return "HELLO !";
    }
}

Cette classe expose publiquement deux méthodes qui constituent deux points de terminaison. En utilisant la méthode MapControllerRoute, voici comment seront accessibles ces deux points de terminaison:

  • SayHello - La méthode SayHello est exécutée lorsque l'URL Hello/SayHello est envoyée dans une requête.
  • YellHello - La méthode YellHello est exécutée lorsque l'URL Hello/YellHello est envoyée dans une requête.

Comment ça marche alors ?

MapControllerRoute utilise ce pattern-ci : {controller=Home}/{action=Index}/{id?}.

Dans le pattern décrit ci-dessus, sont substituées les valeurs entre accolades pour obtenir une route vers un point de terminaison. Dans l'URL, {controller=Home} sera remplacé par le nom d'un contrôleur, {action=Index} sera remplacée par ... une action (une méthode publique d'un contrôleur), et {id?} est un paramètre optionnel (d'où la présence du point d'interrogation).

Si aucune valeur n'est spécifiée à la place de {controller} et {action}, la valeur qu'ils prendront par défaut se situe après le signe =. Dans le cas de MapControllerRoute, si rien n'est spécifié à la place de {controller}, il prendra la valeur Home, et si rien n'est spécifié à la place de {action}, il est substitué par Index.

Ainsi, lorsque tu appelles l'URI racine du site (/), l'itinéraire emprunté suivra la route /Home/Index. Et si tu demande l'URI /Home, l'action entreprise sera Index.

Le nom de la classe du contrôleur étant HelloController, ASP.Net va extraire tout ce qui se situe avant Controller pour créer un itinéraire conventionnel. Dans notre cas, ASP.Net va extraire le mot Hello et créer un itinéraire vers les actions de HelloController.

Cette convention est finalement très, très simple: le nom du contrôleur et celui de ses actions vont être extraites puis un itinéraire pour chacun des points de terminaison va être tracé.

Les définitions de contrôleurs doivent respecter quelques conventions pour que le routage fonctionne (tiens donc !).

  • Les fichiers définissant un contrôleur doivent se situer dans le dossier /Controllers
  • La classe déclarée doit l'être dans l'espace de noms Controllers.
  • Le nom de classe doit se terminer par Controller.

Si ces conventions ne sont pas respectées, ASP.Net ne saura pas comment créer les itinéraires de manière automatique. C'est là le désavantage du routage par convention: les routes du site doivent suivre une convention rigide qui de plus révèle beaucoup d'informations sur l'architecture de l'application - architecture contenant de précieuses informations pour les pirates malveillants.

↗
Routage conventionnel

Documentation officielle sur le routage conventionnel

docs.microsoft.com

Par attribut

Le routage par attribut est de loin plus flexible que le routage par convention, mais aussi plus verbeux. Alors que le routage par convention se configure quasi-uniquement - à l'exception de conventions à respecter au niveau de la définition de contrôleurs - dans la méthode Program.Main, le routage par attribut se configure au niveau des définition du contrôleur et de ses actions.

Rien ne vaut mieux qu'un exemple ! Reprenons l'exemple précédent mais cette fois en mettant en place un routage par attribut.

/Program.cs

csharp
public static void Main(string[] args)
{
    [...]
    builder.Services.AddControllersWithViews(); // Of course you need to inject controllers service

    // [...] Other middlewares in the pipeline

    app.UseRouting();

    // [...] Other middlewares in the pipeline

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

    [...]
}

/Controllers/HelloController.cs

csharp
public class HelloController
{
    [Route("/Say")] // The endpoint here is reached by calling `/Say` route
    public String SayHello()
    {
        return "Hello !";
    }

    [Route("/Yell")] // The endpoint here is reached by calling `/Yell` route
    public String YellHello()
    {
        return "HELLO !";
    }
}

Ci-dessus, on déclare un contrôleur mettant deux points de terminaison à disposition. La différence par rapport au routage par convention est que les routes sont déclarées directement dans le corps du contrôleur, ceci afin de pouvoir paramétrer finement les routes empruntées. Le routage par attribut est par essence plus verbeux que le routage conventionnel car il nécessite de définir pour chaque point de terminaison la route à emprunter.

Ainsi:

  • La méthode SayHello est accessible par la route /Say
  • La méthode YellHello est accessible par la route /Yell

Raccourcir les attributs

Cependant - et par commodité - il est possible de raccourcir les déclarations de routes - pratique lorsqu'on a plus de 10 points de terminaison pour un contrôleur. Voici un exemple:

csharp
[Route("/{action}")]
public class HelloController
{
    public String SayHello()
    {
        return "Hello !";
    }

    public String YellHello()
    {
        return "HELLO !";
    }
}

Ici, {action} va être substituée par le nom d'une action d'un contrôleur, comme dans la partie précédente sur le routage par convention.

↗
Routage par attribut

Documentation officielle sur le routage par attribut dans les API Rest.

docs.microsoft.com

Challenge

Durant ce challenge tu vas mettre en place une application ASP.Net qui permet, comme dans l'exemple utilisé dans cette quête, de mettre à disposition un contrôleur définissant deux actions, YellHello et SayHello.

Yell et Say doivent être accessibles via deux itinéraires différents:

  • un itinéraire conventionnel
  • un itinéraire par attribut

Critères de validation

  • La base du projet est générée à l'aide de Visual Studio 2019 en initialisant un projet ASP.Net Core avec API REST et contrôleurs
  • Un lien vers un dépôt GitHub est fourni en tant que solution du challenge
  • Copie-colle la classe HelloController dans un fichier /Controllers/HelloController.cs
  • Un itinéraire conventionnel ({controller}/{action}) permet d'accéder aux deux action Yell et Say
  • Un itinéraire par attribut déclarant des routes différentes par rapport à l'itinéraire conventionnel permet d'accéder aux deux actions Yell et Say