Accueil Nos publications Blog [ASP.NET Core] Trucs et astuces #1

[ASP.NET Core] Trucs et astuces #1

ASP.NET logoDans ce billet, je vous propose de découvrir quelques astuces autour d’ASP.NET Core. Il ne s’agit pas de fonctionnalités cruciales mais plutôt de petites variantes de fonctionnement méconnues et trop peu documentées jusqu’à présent.

Attention, cet article a été écrit à partir de la beta8 d’ASP.NET Core. Les APIs décrites ici sont encore sujettes à changement d’ici la release finale de la plate-forme.

Le fichier launchsettings.json

Vous voulez cibler une version spécifique du framework pour un profil de compilation donné ? Ou bien définir une variable d’environnement pour ce même profil ? Le fichier launchsettings.json est là pour ça.

Dans l’exemple ci-dessous, j’associe au profil test la commande toto. Je précise également qu’avec ce profil, je veux que la variable d’environnement ASPNET_ENV contienne la valeur Testing.

{
  "profiles": {
    "test": {
      "commandName": "toto",
      "environmentVariables": {
        "ASPNET_ENV": "TESTING"
      }
    }
  }
}

L’injection de paramètres dans le constructeur de la classe Startup

La classe Startup, point d’entrée principal de votre application ASP.NET 5 peut se faire injecter différents services par son constructeur. Il ne s’agit pas vraiment d’injection de dépendances au sens noble du terme puisque, ici, il n’y a pas de résolution de paramètres depuis un conteneur IoC, ce dernier étant créé un peu plus tard dans le cycle de vie de l’application.

En fait, c’est le mécanisme qui invoque la classe Startup qui connaît (comprenez qu’il référence en dur) plusieurs surcharges du constructeur de la classe.

Techniquement, il est ainsi capable d’injecter trois types de services :

  • IApplicationEnvironment – Elle contient des informations sur l’application en tant que telle : son nom, sa version, le nom du dossier racine pour le contenu client, le chemin physique vers l’application. Cette interface peut être vue comme un point d’accès à une partie du contenu du fichier project.json.

  • IHostingEnvironment – Elle contient différentes informations sur l’environnement d’hébergement de l’application (le type d’hôte, l’alias de la plate-forme, les features de l’hôte) et plusieurs méthodes d’extension sur lesquelles je reviendrai plus tard.

Les trois constructeurs suivants sont donc des variantes possibles dans l’écriture du constructeur de la classe Startup. Toutes les surcharges sont fonctionnelles.

public Startup()
{
}

public Startup(IApplicationEnvironment applicationEnvironment)
{
}

public Startup(IApplicationEnvironment applicationEnvironment, IHostingEnvironment hostingEnvironment)
{
}

De la même manière, la méthode Configure peut se faire injecter différents paramètres.

La surcharge la plus couramment utilisée est celle prenant une instance de IApplicationBuilder en tant que paramètre. C’est cette interface qui permet d’attacher des middlewares au tunnel de traitement des requêtes.

public class Startup
{
    public void Configure(IApplicationBuilder applicationBuilder)
    {
    }
}

Une alternative est possible où l’interface IHostingEnvironment est elle aussi injectée lors de l’appel à la méthode Configure.

public class Startup
{
    public void Configure(IApplicationBuilder applicationBuilder, IHostingEnvironment hostingEnvironment)
    {
    }
}

Une classe Startup par environnement

La classe Startup, point d’entrée d’une application ASP.NET Core ne doit pas nécessairement s’appeler Startup. En fait, le runtime va rechercher en priorité une classe Startup suffixée du nom de l’environnement courant.

Le nom de l’environnement peut être récupéré par le biais de l’interface IHostingEnvironment (cf. la section précédente de ce billet). Il est récupéré par le biais de la variable d’environnement ASPNET_ENV, ou par la clé de configuration Hosting:Environment. Par défaut, dans l’expérience Visual Studio, la valeur définie est Development.

Ainsi, il est possible de créer une classe StartupDevelopment.

Faîtes l’essai. Ajoutez les deux classes ci-dessous à un projet ASP.NET Core. Placez un breakpoint dans chacune des méthodes Configure et démarrez l’application. Le programme s’arrêtera automatiquement dans la méthode Configure de la classe StartupDevelopment.

public class StartupDevelopment
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseIISPlatformHandler();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseIISPlatformHandler();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

Il est à noter que cette approche est également utilisée dans la recherche des méthodes Configure et ConfigureServices de la classe Startup. Cela signifie que, plutôt que d’avoir une classe Startup radicalement différente, il est possible de ne créer que des méthodes Configure et ConfigureServices alternatives.

Dans ce cas, la syntaxe à utiliser est celle présentée dans l’exemple ci-dessous. Notez qu’ici, j’ai fait le choix de créer une alternative pour les deux méthodes mais qu’il est également possible de n’écrire une alternative que pour Configure ou ConfigureServices.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
    }
    public void ConfigureDevelopmentServices(IServiceCollection services)
    {
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseIISPlatformHandler();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }

    public void ConfigureDevelopment(IApplicationBuilder app)
    {
        app.UseIISPlatformHandler();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

Les méthodes d’extension de IHostingEnvironment

Je parle de cette interface depuis le début du billet car elle est extrêmement intéressante. Techniquement, elle remplace des éléments qui n’étaient pas gérés nativement par ASP.NET dans ses versions précédentes ou d’une façon n’étant pas réellement compatible avec la testabilité d’une application.

Le paquet Microsoft.AspNet.Hosting.Abstractions contient une classe avec plusieurs valeurs correspondants à les typologies d’environnement les plus courantes.

public static class EnvironmentName
{
    public static readonly string Development = "Development";
    public static readonly string Staging = "Staging";
    public static readonly string Production = "Production";
}

Et également plusieurs méthodes d’extension sur l’interface IHostingEnvironment encapsulant l’accès à ces noms d’environnement : IsDevelopment ou IsProduction.

Ainsi, il est possible d’utiliser ces méthodes pour conditionner le branchement de certains services dans le conteneur IoC, ou pour conditionner l’utilisation d’un middleware, ou même n’importe quelle fonctionnalité d’une application puisque l’interface IHostingEnvironment peut se faire injecter dans n’importe quel élément d’une application ASP.NET Core.

L’exemple suivant utilise la méthode d’extension IsDevelopment pour conditionner l’utilisation de BrowserLink mais également pour choisir le mode de gestion des exceptions. Si l’application est exécutée sur un environnement de développeur alors, en cas d’erreur, le détail de l’exception sera affiché. Si l’application est exécutée sur un environnement qui n’est pas un environnement de développement, alors c’est un contrôleur et une action particulière qui seront utilisés pour servir une page d’erreur personnalisée. Notez que ce comportement correspond au nœud CustomErrors du web.config que l’on pouvait modifier dans les applications ASP.NET historiques.

// Add the following to the request pipeline only in development environment.
if (env.IsDevelopment())
{
    app.UseBrowserLink();
    app.UseDeveloperExceptionPage();
    app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll);
}
else
{
    // Add Error handling middleware which catches all application specific errors and
    // sends the request to the following path or controller action.
    app.UseExceptionHandler("/Home/Error");
}

Une autre méthode d’extension présente sur l’interface IHostingEnvironment est la méthode MapPath. Celle-ci permet de résoudre un chemin physique à partir d’un chemin relatif à l’environnement d’exécution de l’application ASP.NET. Il s’agit de l’équivalent de la méthode statique MapPath présente sur la classe Server, c’est-à-dire l’approche utilisée jusqu’à présent. Encore une fois, l’interface IHostingEnvironment est injectable n’importe où, cette nouvelle approche étant totalement optimisée pour rendre une application testable.

var physicalPath = env.MapPath("~/data/mydata.json");

Conclusion

J’espère que ce billet a pu vous apprendre deux ou trois petites astuces sur les variantes de fonctionnement d’ASP.NET Core. Si vous avez des questions sur ces points ou d’autres concernant ASP.NET Core, n’hésitez pas à me contacter ou à les poser directement dans un commentaire.