Accueil Nos publications Blog [ASP.NET Core] Gestion de la configuration

[ASP.NET Core] Gestion de la configuration

ASP.NET logoAvec ASP.NET Core, le mécanisme de gestion de la configuration d’une application Web a été profondément modifié. Cette refonte touche à la fois à la source de la configuration mais aussi à son format de description. Ce billet est pour moi l’occasion de comparer l’ancienne approche avec la nouvelle API.

La configuration historique d’une application Web

Historiquement, l’utilisation d’un fichier de configuration dans une application Web passe par un fichier web.config.

Celui-ci a une structure fixe et imposée. C’est à dire que les développeurs disposent de certaines zones simples d’utilisation (les chaines de connexion ou les appsettings) respectant un schéma bien spécifique. Pour les besoins plus complexes, il devient nécessaire de créer des sections personnalisées dont le modèle de programmation peut sembler un peu lourd d’utilisation.

La manipulation de ce fichier de configuration se fait au travers de la classe statique ConfigurationManager. S’agissant d’un élément statique, la testabilité du code en est fortement impactée. Il convient donc généralement d’encapsuler sa manipulation dans un service dont l’écriture est à la charge du développeur.

Enfin, ce fichier est un fichier XML. Il est impossible d’utiliser un autre format. De plus, dans le cas d’une application Azure, le modèle de programmation et la gestion des configurations ne sont pas unifiés. Il faut donc dupliquer le code pour gérer les différents environnements.

L’API de configuration d’ASP.NET Core

ASP.NET 5 introduit de nouveaux formats pour les fichiers configuration. L’écriture de ces fichiers ne passe plus automatiquement par XML mais peut prendre de nouvelles formes : JSON, INI ou encore variables d’environnements. A noter que chaque fichier peut prendre le nom que vous préférez, il n’est plus obligatoire de le nommer web.config. Cependant, il est nécessaire de le placer à la racine de l’application Web.

Un premier exemple avec un fichier XML.

<config>
    <ConnectionStrings>
        <XmlConnectionString>todo</XmlConnectionString>
    </ConnectionStrings>
</config>

Le même fichier de configuration décliné sous la forme d’un fichier JSON.

{
    "ConnectionStrings": {
        "JsonConnectionString": "todo"
    }
}

Enfin, un dernier exemple avec un fichier INI.

[ConnectionStrings]
IniConnectionString = todo

La nouvelle API se base autour du paquet NuGet Microsoft.Framework.Configuration.Abstractions. Après l’avoir ajouté, il est nécessaire de référencer l’espace de nom Microsoft.Framework.Configuration. Ces deux ajouts vont nous permettre d’utiliser un objet de type IConfiguration, lui-même étant construit à partir d’une instance de ConfigurationBuilder.

Pour fonctionner, un ConfigurationBuilder a besoin d’un répertoire de travail, c’est à dire l’emplacement où il doit rechercher les fichiers de configuration. Pour récupérer l’emplacement actuel de l’application en cours d’exécution, il est possible de se recevoir par injection dans le constructeur de la classe Startup une instance de IApplicationEnvironment. Cette dernière est disponible grâce au paquet Microsoft.Framework.Runtime.Abstractions.

L’exemple de code ci-dessous est une première approche d’une application ASP.NET Core prête à récupérer des informations de configuration.

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Configuration;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Runtime;

namespace WebApplication12
{
    public class Startup
    {
        private readonly IConfiguration _configuration;

        public Startup(IApplicationEnvironment applicationEnvironment)
        {
            var configurationBuilder = new ConfigurationBuilder(applicationEnvironment.ApplicationBasePath);

            _configuration = configurationBuilder.Build();
        }
    }
}

Par défaut, sans paquets additionnels, il est uniquement possible de référencer des éléments de configuration depuis un fichier INI, de variables d’environnements ou de paramètres passés via la ligne de commande.

L’exemple ci-dessous permet de référencer un fichier INI.

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Configuration;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Runtime;

namespace WebApplication12
{
    public class Startup
    {
        private readonly IConfiguration _configuration;

        public Startup(IApplicationEnvironment applicationEnvironment)
        {
            var configurationBuilder = new ConfigurationBuilder(applicationEnvironment.ApplicationBasePath);

            configurationBuilder.AddIniFile("configuration.ini");

            _configuration = configurationBuilder.Build();
        }
    }
}

En ajoutant le paquet Microsoft.Framework.ConfigurationModel.Json, il devient alors possible d’inclure un fichier de configuration basé sur JSON.

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Configuration;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Runtime;

namespace WebApplication12
{
    public class Startup
    {
        private readonly IConfiguration _configuration;

        public Startup(IApplicationEnvironment applicationEnvironment)
        {
            var configurationBuilder = new ConfigurationBuilder(applicationEnvironment.ApplicationBasePath);

            configurationBuilder.AddIniFile("configuration.ini");
            configurationBuilder.AddJsonFile("configuration.json");

            _configuration = configurationBuilder.Build();
        }
    }
}

Et sur le même principe, il est possible d’utiliser un fichier XML avec le paquet NuGet Microsoft.Framework.Configuration.Xml.

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Configuration;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Runtime;

namespace WebApplication12
{
    public class Startup
    {
        private readonly IConfiguration _configuration;

        public Startup(IApplicationEnvironment applicationEnvironment)
        {
            var configurationBuilder = new ConfigurationBuilder(applicationEnvironment.ApplicationBasePath);

            configurationBuilder.AddIniFile("configuration.ini");
            configurationBuilder.AddJsonFile("configuration.json");
            configurationBuilder.AddXmlFile("configuration.xml");

            _configuration = configurationBuilder.Build();
        }
    }
}

L’arborescence dans un fichier de configuration est libre. Une clé est ensuite accessible en précisant les différents noms de l’arborescence séparés par “:”.

Si une même clé est présente dans deux sources de configuration, c’est la dernière source qui l’emporte.

Par exemple, avec les deux fichiers de configuration suivants.

{
  "une": {
      "cle":  "valeurjson"
  }
}

<config>
  <une>
    <cle>valeurxml</cle>
  </une>
</config>

Et le programme ci-dessous qui consomme la configuration.

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Configuration;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Runtime;

namespace WebApplication12
{
    public class Startup
    {
        private readonly IConfiguration _configuration;

        public Startup(IApplicationEnvironment applicationEnvironment)
        {
            var configurationBuilder = new ConfigurationBuilder(applicationEnvironment.ApplicationBasePath);

            configurationBuilder.AddJsonFile("configuration.json");
            configurationBuilder.AddXmlFile("configuration.xml");

            _configuration = configurationBuilder.Build();
        }

        public void ConfigureServices(IServiceCollection serviceCollection)
        {
        }

        public void Configure(IApplicationBuilder app)
        {
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync(_configuration["une:cle"]);
            });
        }
    }
}

Une requête HTTP sur l’application affichera alors le texte “valeurxml”.

Grâce à ce mécanisme qui permet surcharger des clés en fonction de l’ordre d’inclusion des fichiers de configuration, il est alors possible d’imaginer un système en se basant sur le nom de l’environnement actuel pour charger un fichier de configuration spécifique. Ce nom d’environnement peut être défini dans les propriétés du projet. Il est ensuite récupérable via l’interface IHostingEnvironment. Cette dernière peut également être injectée dans le constructeur de la classe Startup.

L’exemple ci-dessous illustre ce principe. Notez l’usage de la chaîne de formatage avec C#6.

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Configuration;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Runtime;

namespace WebApplication12
{
    public class Startup
    {
        private readonly IConfiguration _configuration;

        public Startup(IHostingEnvironment hostingEnvironment, IApplicationEnvironment applicationEnvironment)
        {
            var configurationBuilder = new ConfigurationBuilder(applicationEnvironment.ApplicationBasePath);

            configurationBuilder.AddJsonFile("configuration.json");
            configurationBuilder.AddJsonFile($"configuration.{hostingEnvironment.EnvironmentName}.json", optional:true);

            _configuration = configurationBuilder.Build();
        }

        public void ConfigureServices(IServiceCollection serviceCollection)
        {
        }

        public void Configure(IApplicationBuilder app)
        {
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync(_configuration["une:cle"]);
            });
        }
    }
}

Conclusion

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

Ce nouveau système de configuration est bien plus souple que le modèle historique. De plus, il devrait augmenter la testabilité du code d’une application ASP.NET Core. Il rentre donc totalement en cohérence avec tous les autres efforts faits en ce sens par les équipes de Microsoft.

A bientôt