Accueil > Comment configurer une application ASP.NET Core ?
Sylvain Sidambarom
7 juillet 2022
Read this post in English

Comment configurer une application ASP.NET Core ?

Comment configurer une application ASP.NET Core

Dans cet article, nous vous proposons de voir comment la configuration fonctionne dans ASP.NET Core. Comment nous pouvons définir différentes sources de configuration, comment lire les clés de configuration qui leur sont associées et comment ces différentes sources sont prises en compte dans le framework. Nous mettrons en évidence des cas associés à la prise charge des clés de configuration par ASP.NET Core.

 

Lire une configuration depuis l’appsettings.json

Nous allons créer un projet ASP.NET Core API dans lequel nous effectuerons l’ensemble de nos manipulations.

dotnet new mvc -n Configuration.AspNet.Core.Lab

Une fois l’application créée, nous avons tenté de lire notre première configuration. Pour ce faire, ajoutons dans le fichier appsettings.json, la configuration personnalisée string-key avec la valeur value-from-appsettings.json comme suit :

 

{
  "AllowedHosts": "*",
  "string-key": "value-from-appsettings.json" // new custom configuration
}

 

Afin de lire la nouvelle configuration, nous ajoutons dans le Program.cs une console.WriteLine() avec la clé de configuration comme suit :

var builder = WebApplication.CreateBuilder(args);

// Configuration should be available here
IConfiguration configuration = builder.Configuration;
System.Console.WriteLine("string-key => " + configuration["string-key"]);

// Some code here ...

app.Run();

 

L’objet « builder.Configuration » donne accès à la clé de configuration que nous avons ajouté grâce au getter » Configuration[‘NomDeMaClé’] ».

Nous pouvons voir dans le retour de la console, après avoir lancé l’application, le résultat suivant :

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:04.16
Building...
string-key => value-from-appsettings.json

 

Et voilà ! Nous venons de créer et lire une nouvelle configuration avec ASP.NET Core !

Mais comment fonctionne exactement ce mécanisme ? C’est ce que nous vous proposons de regarder dans la section suivante.

 

Comment fonctionne la configuration dans ASP.NET Core ?

Dans le Program.cs, la méthode CreateBuilder est appelée :

var builder = WebApplication.CreateBuilder(args);

 

Elle enregistre les sources de configuration par défaut d’ASP.NET Core grâce à l’appel dans le framework de la méthode CreateDefaultBuilder. Cette dernière initialise différents providers, alimentés par les sources de configuration.

Le provider est un objet qui permet la lecture des clés de configuration trouvées dans la source. Un fois le provider initialisé, il est ajouté dans une collection de providers du ConfigurationManager (cf. Builder.Configuration). Ci-dessous est présenté le code permettant d’initialiser la liste des providers :

 

Les sources de configuration sont ajoutées par défaut, dans le ConfigurationManager, dans l’ordre suivant :

  1. AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)

    Initialise et ajoute un provider avec les clés de configuration du fichier JSON appsettings.json (Point A). Le paramètre optional indique que le provider ne sera pas ajouté si le fichier n’a pas à être présent. Enfin reloadOnChange permet de réinitialser le provider si le fichier appsettings.json change.

  2. AddJsonFile($"{env.EnvironmentName}.json", optional: true, reloadOnChange: true)

    Initialise et ajoute un provider avec les clés de configuration du fichier JSON appsettings.{env.EnvironmentName}.json (Point B). La valeur de env.EnvironmentName est définie dans la variable d’environnement ASPNETCORE_ENVIRONMENT du fichier ./Properties/launchSettings.json. Nous en reparlerons dans la section suivante

  3. AddUserSecrets(appAssembly, optional: true)

    Initialise et ajoute un provider avec les clé de configuration du User secret (Point C) : cette notion sera développer un peu plus tard

  4. AddEnvironmentVariables()

    Initialise et ajoute un provider avec les clés de configuration issue des variables d’environnement (Point D) . Nous verrons en détail comment cela se manifeste.

  5. AddCommandLine(args)

    Initialise et ajoute un provider avec les clés de configuration des arguments de commande (Point E). Ce sont les arguments passés lors de l’exécution de la commande dotnet run –arg1 et permettant de lancer votre application

 

Quand l’ensemble des providers du ConfigurationManager est initialisé, il possible de lire les clés de configuration grâce au getter suivant :

IConfiguration[“TheKeyIAmLookingFor” ];

Ce getter va chercher la valeur de la clé de configuration de la façon suivante.

 

Getter ASP configuration

 

Ce schéma illustre la recherche d’une clé de configuration dans ASP.NET Core.

Lorsque l’on recherche la valeur d’une clé, le ConfigurationManager demande au dernier provider de sa collection de providers, s’il possède la valeur de la clé. Si c’est le cas, il retourne la valeur, sinon il propage la demande au provider qui le précède dans la collection.

Dès lors la lecture des valeurs des clés de configuration est faite sur le principe du LIFO (Last In Fist Out). La dernière value d’une clé ajoutée dans le ConfigurationManager via le provider est la seule utilisée. C’est par ce mecanisme que le framework priorise les valeurs de ses clé de configuration.

Mettons en pratique cette lecture par priorité en commençant par l’appsettings d’environnement.

 

Lire une configuration depuis l’appsettings de l’environnement

 

Si vous observez la structure du projet, vous constaterez qu’il y a un fichier appsettings.json et un fichier appsettings.Development.json. Comment nous l’avons vu précédemment, la priorité des configuration providers fait que les configurations définies dans appsettings.Development.json seront prises en compte par rapport à appsettings.json.

Pour illustrer cela, nous ajoutons la même configuration dans appsettings.Development.json en modifiant la valeur et nous ajoutons de nouvelles configurations de type différent.

{
  "string-key": "value-from-appsettings.Development.json",
  "bool-key": false,
  "int-key": 22,
  "array-string": ["array-dev-value1", "array-dev-value2"],
}

 

Ajoutons dans le Program.cs la lecture des clés manquantes comme ci-dessous.

var builder = WebApplication.CreateBuilder(args);

// Configuration should be available here
IConfiguration configuration = builder.Configuration;
System.Console.WriteLine("env => " + configuration["ASPNETCORE_ENVIRONMENT"]);
System.Console.WriteLine("string-key => " + configuration["string-key"]);
System.Console.WriteLine("bool-key => " + configuration["bool-key"]);
System.Console.WriteLine("int-key => " + configuration["int-key"]);
System.Console.WriteLine("array-string (first item) => " + builder.Configuration["array-string:0"]);
System.Console.WriteLine("array-string (second item) => " + builder.Configuration["array-string:1"]);

// Some code here ...

app.Run();

 

Le résultat suivant montre que bien que la clé string-key est présente dans appsettings.json et appsettings.Development.json, seul la valeur est définie dans appsettings.Development.json est exploitée. Il en est de même pour les autres clés que nous avons ajoutées.

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:04.16
Building...
env => .Development
string-key => value-from-appsettings.Development.json
bool-key => False
int-key => 22
array-string (first item) => array-dev-value1
array-string (second item) => array-dev-value2

 

Nous vous invitons à tester dans un environnement différent de développement (production ou autre). Pensez bien à créer le fichier JSON approprié.

Ce type de configuration est très pratique pour simuler le fonctionnement de l’application dans différents environnements (Développement, Production, etc. ). Par exemple avec l’utilisation d’une base de données en mémoire, l’affichage de logs au niveau debug voireTrace si c’est nécessaire en phase de développement.

A noter : une bonne pratique de configuration de votre application consiste à déclarer toutes vos clés de configuration dans l’appsettings.json avec des valeurs par défaut, et modifier la valeur de la clé souhaitée dans le fichier d’environnement approprié. Cela assure une meilleure lisibilité et une meilleure compréhension du fonctionnement de l’application. Prenons un exemple où votre application utilise un service externe : dans l’appsettings.json sera définie l’url de production de ce service et dans l’appsettings.Development.json sera définie l’url d’autres environnements du service ou un mock.

 

Lire une configuration depuis les secrets de l’utilisateur

Le secret d’utilisateur est un mécanisme qui permet de stocker des configurations sensibles sur votre machine locale, au cours du développement, au travers d’un outil CLI (Command Line Interface) appelé user-secrets. Les secrets ainsi gérés par lui sont stockés dans un autre emplacement que celui du projet qui les utilise et ne sont donc pas source-contrôlés. Par ailleurs les secrets peuvent être partagés entre plusieurs projets par référencement dans le « .csproj ».

Par défaut d’ASP.NET Core n’initialise un provider avec les secrets d’utilisateur qu’en environnement de développement (cf Point C du code sur l’ajout des provider).

⚠️  Attention : la configuration stockée dans les secrets d’utilisateur ne est pas chiffrée. Elle ne devrait être utilisée que pour des besoins de développement.

 

Fonctionnement des secrets d’utilisateur via la CLI

Dans le répertoire de votre projet, lancer la commande ci-dessous.

dotnet user-secrets init

La clé UserSecretsId est ajoutée dans YourProjectName.csproj par la CLI (Command Line Interface) user-secrets. Il s’agit d’un outil dotnet qui permet de gérer de secret en local. Le Guid UserSecretsId identifie le fichier secret.json sur votre machine où les secrets seront enregistrés. Ci-dessous un exemple de csproj où les secrets d’utilisateur ont été initialisés.

 

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>

    <TargetFramework>net6.0</TargetFramework>

    <Nullable>enable</Nullable>

    <ImplicitUsings>enable</ImplicitUsings>

    <RootNamespace>MyProject</RootNamespace>

    <UserSecretsId>0084a804-ad71-4a3b-ba35-783bc3018666</UserSecretsId>

  </PropertyGroup>

</Project>

 

Le fichier où les secrets sont stockés se trouve dans le répertoire suivant :

  • %APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secret.json (sur Window)
  • ~/.microsoft/usersecrets/<user_secrets_id>/secrets.json (sur linux)

Nous pouvons ainsi ajouter une configuration dans le secret.json, comme le montre les commandes ci-dessous :

dotnet user-secrets set string-key « value-from-secrets.json »
dotnet user-secrets set bool-key false
dotnet user-secrets set int-key 33
dotnet user-secrets set « array-string:0 » « array-value1-from-secrets  »
dotnet user-secrets set « array-string:1 » « array-value2-from-secrets  »
dotnet user-secrets set « object-key:key1 » « value1-from-secrets »
dotnet user-secrets set « object-key:key2 » « value2-from-secrets »

Les commandes précédentes initialisent les clés de configuration de la façon suivante :

Les commandes précédentes initialisent les clés de configuration de la façon suivante :

  • string-key avec la chaîne de caractères value-from-secrets.json
  • bool-key avec le booléen false
  • int-key avec le nombre 33
  • array-string:0 avec comme premier élément la chaîne de caractères array-value1
  • array-string:1 avec comme second élément la chaîne de caractères array-value2
  • object-key:key1 avec la chaîne de caractères value1-from-secrets
  • object-key:key2 avec la chaîne de caractères value2-from-secrets

Maintenant que nos secrets d’utilisateur sont saisis, observons le contenu du fichier secrets.json.

{
  "string-key": "value-from-secrets.json",
  "object-key:key2": "value2-from-secrets",
  "object-key:key1": "value1-from-secrets",
  "int-key": "33",
  "bool-key": "false",
  "array-string:0": "array-value1-from-secrets",
  "array-string:1": "array-value2-from-secrets",
}

 

Vous noterez que l’ensemble des configurations sont enregistrées à plat, c’est-à-dire en clé-valeur. Cela est dû à la CLI qui ne peut manipuler les configurations qu’en clé-valeur, mais il est possible de stocker les configurations par section comme dans le fichier appsettings.json (cf. Section du niveau des logs)en modifiant manuellement le fichier.

Ce type de configuration est très utile pour stocker des identifiants de services externes sans que ceux-ci ne soient présents dans le code source, ou des clé de chiffrement en phase développement.

A noter : Visual Studio permet d’accéder directement un fichier en faisant un clic droit sur le fichier projet (.csproj) puis en cliquant sur Manage User Secrets.

 

Manage User Secrets Visual Studio

 

Lire la configuration des secrets d’utilisateur

Partons de notre projet préalablement créé et initialisons les secrets d’utilisateurs et ajoutons ensuite les lignes suivantes dans Program.cs afin de lire les clés de configuration que nous avons stockées dans notre secrets.json:

Console.WriteLine("Read 'string-key' from Program.cs => " + builder.Configuration["string-key"]);
Console.WriteLine("Read 'bool-key' from Program.cs => " + builder.Configuration["bool-key"]);
Console.WriteLine("Read 'int-key' from Program.cs => " + builder.Configuration["int-key"]);
Console.WriteLine("Read 'array-string' from Program.cs (first item) => " + builder.Configuration["array-string:0"]);
Console.WriteLine("Read 'array-string' from Program.cs (second item) => " + builder.Configuration["array-string:1"]);
System.Console.WriteLine("key1 in section object-key from Program.cs => " + builder.Configuration["object-key:key1"]);
System.Console.WriteLine("key1 in section object-key from Program.cs => " + builder.Configuration["object-key:key2"]);

 

Au lancement de l’application en développement, nous retrouvons bien nos valeurs initialisées plus tôt avec la CLI dans le retour console.

Read 'string-key' from Program.cs => value-from-secrets.json
Read 'bool-key' from Program.cs => false
Read 'int-key' from Program.cs => 33
Read 'array-string' from Program.cs (first item) => array-value1-from-secrets
Read 'array-string' from Program.cs (second item) => array-value2-from-secrets
key1 in section object-key from Program.cs => value1-from-secrets
key1 in section object-key from Program.cs => value2-from-secrets

 

Nous constatons bien la valeur de la clé string-key est value-from-secrets.json. Hors, cette clé a déjà été définie dans l’appsettings.json et dans le l’appsettings.Deveploment.json.

Nous observons bien ici la priorité des clés de configuration des secrets d’utilisateur sur les clés des différents fichiers de appsettings en environnement de développement.

 

Lire une configuration depuis les variables d’environnement

Continuons notre exploration, essayons à présent de lire une configuration depuis les variables d’environnement. Dans notre appsettings.json nous avons la clé string-key.

{
  "string-key": "value-from-appsettings.json",
}

 

Essayons de lire des valeurs issues des variables d’environnement.

 

Initialisation de la variable d’environnement

Dans un terminal PowellShell Core, saisissez les commandes suivantes pour initialiser votre variable d’environnement et pour la lire :

${env:string-key} = "value-from-environment"
${env:bool-key} = true
${env:int-key} = 44
${env:array-string__0} = "array-value1-from-environment"

 

Ou initialisez vos variablse d’environnement manuellement comment décrit ci-dessous sous Windows :

1. Ouvrez le Panneau de contrôle de gestion des variables d’environnement dans la fenêtre Propriété du système environnement.

Windows fenêtre de propriété

 

2. Créez une nouvelle variable d’environnement

Création variable d'environnement

 

Nouvelle variable d'environnement Windows

 

3. Confirmez que cette dernière a bien été créée.

Vérification création variable d'environnement

 

Lancez de nouveau l’application :

Building...
env => Development
Read 'string-key' from Program.cs => value-from-environment
Read 'bool-key' from Program.cs => true
Read 'int-key' from Program.cs => 44
Read 'array- string ' from Program.cs (first item) => array-value1-from-environment
Read 'array-string' from Program.cs (second item) => array-value2-from-secrets
key1 in section object-key from Program.cs => value1-from-secrets
key1 in section object-key from Program.cs => value2-from-secrets

 

Nous observons bien que les clés de configuration stockées dans les variables d’environnement sont bien prises en compte. De plus, nous constatons également qu’elles sont bien prioritaires par rapport aux définies dans les secrets d’utilisateur comme nous le montre les valeurs de string-key, bool-key, int-key, array-string__0. La valeur de object-key elle est récupérée des secrets d’utilisateur car elle n’est pas initialisée dans les variables d’environnement.

Les clés de configuration définies dans les variables d’environnement sont très utiles pour définir une configuration spécifique à la machine l’application est lancé. Exemple typique est la connectionStrings à la base de données.

A noter : pour que la variable d’environnement soit bien prise en compte, vous pouvez être amené à redémarrer votre IDE.

Notez également que pour accéder aux valeurs des clés associées au tableau ou un objet, nous utilisons le séparateur « : » dans le getter du ConfigurationManager pour lire les valeurs. Cependant ce séparateur n’est pas supporté lorsque l’on souhaite définir les variables d’environnement, il doit être remplacé par « __ » : il s’agit de 2 underscores consécutifs.

 

Lire une configuration depuis les arguments de commande

 

Pour cette partie, l’idée est simple : déclarez ce qe vous lisez, en lançant la commande « dotnet run » avec en argument notre configuration.

dotnet run string-key="value-from-command-line" array-key:1: "array-value-from-command-line"

Le résultat obtenu est le suivant :

Building...
env => Development
Read 'string-key' from Program.cs => value-from-command-line 
Read 'bool-key' from Program.cs => true
Read 'int-key' from Program.cs => 44

Read 'array- string ' from Program.cs (first item) => array-value-from- environment

Read 'array-string' from Program.cs (second item) => array-value2-from-command-line
key1 in section object-key from Program.cs => value1-from-secrets
key1 in section object-key from Program.cs => value2-from-secrets


Nous observons que les clés déclarées dans la commande dotnet run prennent bien le pas des clés déclarées dans les autres sources. Cela illustre bien que les clés de configuration déclarées en argument de commande sont les plus prioritaires.

Cela permet de forcer la valeur d’une clé de configuration et de modifier de comportement de l’application sans toucher les binaires ni la configuration déployée. Un cas d’usage typique est le changement du niveau de log de l’application pour une investigation dans un environnement en cours d’exploitation (dev, test, « pré-prod »).

 

Lire une configuration complexe

Lorsqu’on parle de configuration complexe, on parle de regroupement logique appelé « section ». Dans notre exemple, nous regroupons les configurations précédentes dans une section Example. Les configurations organisées de cette façon s’apparentent à une structure objet.

{
  "Example": {
    "MyString": "value-from-appsettings.json",
    "MyBool": true,
    "MyInt": 22,
    "MyArray": ["array-value1", "array-value2", "array-value3"],
  }    
}

 

La lecture directe

La lecture de chaque valeur se fait en préfixant la clé par le nom de la section avec le séparateur « : » (deux points).

var builder = WebApplication.CreateBuilder(args);

// Configuration should be available here
Console.WriteLine("Read 'MyString' from Program.cs => " + builder.Configuration["Example:MyString"]);
Console.WriteLine("Read 'MyBool' from Program.cs => " + builder.Configuration["Example:MyBool"]);
Console.WriteLine("Read 'MyInt' from Program.cs => " + builder.Configuration["Example:MyInt"]);
Console.WriteLine("Read 'MyArray' from Program.cs (first item) => " + builder.Configuration["Example:MyArray:0"]);
Console.WriteLine("Read 'MyArray' from Program.cs (second item) => " + builder.Configuration["Example:MyArray:1"]);
Console.WriteLine("Read 'MyArray' from Program.cs (third item) => " + builder.Configuration["Example:MyArray:2"]);

// Add services to the container.
builder.Services.AddControllersWithViews();

 

On a ainsi comme résultat :

Building...
Read 'MyString' from Program.cs => value-from-appsettings.json
Read 'MyBool' from Program.cs => False
Read 'MyInt' from Program.cs => 22
Read 'MyArray' from Program.cs (first item) => array-value1
Read 'MyArray' from Program.cs (second item) => array-value2
Read 'MyArray' from Program.cs (third item) => array-value3

 

Nous pouvons noter que toutes les valeurs lues sont des chaînes de caractères.

La lecture via un objet

Il possible de typer fortement notre configuration complexe dans un objet ayant la même structure (nom de propriété et type cohérent). Ici, nous créons un objet ComplexConfigurationOptions avec lequel la configuration sera liée.

public class ComplexConfigurationOptions
{
    public const string SectionName = "Example";
    
    public string MyString { get; set; }
    public bool MyBool { get; set; }
    public int MyInt { get; set; }
    public string[] MyArray { get; set; }
}

NB : mettre le nom de la section de configuration sur laquelle l’objet doit se calquer est une bonne pratique.

 

Lier manuellement sa configuration à un objet

Essayons d’initialiser notre objet ComplexConfigurationOptions avec les valeurs de la section Example via la méthode Bind() de IConfigurationSection;

var myOption = new ComplexConfigurationOptions();
builder.Configuration.GetSection("Example").Bind(myOption);

Console.WriteLine("Read 'string-key' from Option => {0}", myOption.MyString);
Console.WriteLine("Read 'bool-key' from Option => {0}", myOption.MyBool);
Console.WriteLine("Read 'int-key' from Option => {0}", myOption.MyInt);
Console.WriteLine("Read 'array-string' from Option (first item) => {0}", myOption.MyArray[0]);
Console.WriteLine("Read 'array-string' from Option (second item) => {0}", myOption.MyArray[1]);
Console.WriteLine("Read 'array-string' from Option (third item) => {0}", myOption.MyArray[2]);

 

La console montre que toutes les propriétés ayant un nom égal à une clé de configuration, ont été initialisées avec les valeurs correspondantes.

Read ‘string-key’ from Option => value-from-appsettings.json
Read ‘bool-key’ from Option => False
Read ‘int-key’ from Option => 22
Read ‘array-string’ from Option (first item) => array-value1
Read ‘array-string’ from Option (second item) => array-value2
Read ‘array-string’ from Option (third item) => array-value3

Grâce à la liaison, nous avons un objet qui contient toute notre configuration et qui est bien plus simple à manipuler que des chaînes de caractères. Toutefois, il possible de faire mieux en utilisant l’« option pattern ».

 

L’option pattern

L’option pattern permet de typer fortement des configurations dans un objet. Elle permet ainsi de regrouper des configurations de façon logique et indépendante.

builder.Services.Configure<ComplexConfigurationOptions>(builder.Configuration.GetSection(ComplexConfigurationOptions.SectionName));

La méthode Configure<>() est présente dans le package nuget Microsoft.Extensions.Options.ConfigurationExtensions. Par défaut, ce dernier est déjà présent dans ASP.NET Core.

public class HomeController : Controller
{
private readonly ComplexConfigurationOptions _options;

public HomeController(IOptions<ComplexConfigurationOptions> options)
{
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));

Console.WriteLine(« Read ‘MyString’ from options =>  » + _options.MyString);
Console.WriteLine(« Read ‘MyBool’ from options =>  » + _options.MyBool);
Console.WriteLine(« Read ‘MyInt’ from options =>  » + _options.MyInt);
Console.WriteLine(« Read ‘MyArray’ from options =>  » + string.Join(« , « , _options.MyArray));
}
}

Les configurations stockées dans l’objet ComplexConfigurationOptions sont accessibles via l’interface IOptions<T>. Cette dernière est enregistrée dans le gestionnaire de dépendance et est injecté par le framework.

Read 'MyString' from options => value-from-appsettings.json
Read 'MyBool' from options => False
Read 'MyInt' from options => 22
Read 'MyArray' from options => array-value1, array-value2, array-value3

 

Aller plus loin sur ASP.NET Core

 

Nous avons fait un tour d’horizon de la configuration en ASP.NET Core. Nous avons vu comment les sources de configuration sont prises en compte par défaut par le framework grâce à l’initialisation de provider pour chaque source de configuration et comment lire les valeurs. Nous avons vu comment s’illustrait la priorité dans lecture des clés de configuration et quels cas usages.

Enfin, nous nous sommes penchés sur le typage fort des configurations au travers de la liaison manuelle et de l’option pattern qui offre une cohérence et une simplification de l’utilisation des clés configuration dans le code.

 

Pour en savoir plus, nous vous invitons à consulter la documentation suivante :

 

Et pour approfondir le sujet, n’hésitez pas à lire la suite de cet article : Configuration d’ASP .NET Core : l’option pattern

Formation Microsoft AZ204

 

Nos autres articles
Commentaires
Laisser un commentaire

Restez au courant des dernières actualités !
Le meilleur de l’actualité sur le Cloud, le DevOps, l’IT directement dans votre boîte mail.