nullreference.dev

Конфигурация приложений в dotnet

Feb 2025

Основные интерфейсы

.NET предоставляет набор интерфейсов для работы с конфигурациями:

  • IСonfiguration - основной интерфейс для хранения и получения значений конфигурации.

  • IConfigurationSection - представляет собой раздел конфигурации, позволяющий работать с вложенными значениями. Наследуется от IСonfiguration.

  • IConfigurationRoot - корень конфигурации, содержащий список провайдеров и методы для перезагрузки значений. Не рекомендуется к использованию в коде поскольку используемые провайдеры должны быть скрыты.

  • IConfigurationProvider - провайдер конфигурации, который отвечает за чтение данных из различных источников (файлов, переменных окружения и т.д.).

  • IConfigurationBuilder - используется для добавления и настройки провайдеров конфигурации.

Пример использования

Рассмотрим пример использования этих интерфейсов в приложении:

// Входная точка Program.cs с подключением конфигурации
var builder = WebApplication.CreateBuilder(args);
builder.UseConfigs<Program>();

// Файл расширений, вынесен для удобства
public static class ConfigurationBuilderExtensions
{
    public static void UseConfigs<T>(this IHostApplicationBuilder builder) where T : class
        => builder.Configuration.UseConfigs<T>(builder.Environment);
    
    private static void UseConfigs<T>(
        this IConfigurationBuilder builder,
        IHostEnvironment env)
        where T : class
    {
        // Указываем путь к файлам конфигурации
        builder.SetBasePath(Directory.GetCurrentDirectory());
        builder.AddJsonFile("appsettings.json");
        builder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        if (env.IsDevelopment())
        {
            // Используем User Secrets только в режиме разработки
            builder.AddUserSecrets<T>(optional: true);
        }
        
        // Подключение переменных окружения с использованием префикса
        builder.AddEnvironmentVariables("CE_");
    }
}

Порядок добавления провайдеров важен, так как последующие провайдеры имеют более высокий приоритет. Каждый провайдер может добавлять свои значения, частично или полностью перекрывая предыдущие.

Использование нескольких конфигурационных файлов

Можно использовать разные файлы конфигурации для разных окружений. Например, можно иметь несколько файлов appsettings.json, appsettings.Development.json и appsettings.Production.json.

  • В файле appsettings.json общие настройки для всех сред.

  • В файле appsettings.Development.json настройки для режима разработки (например, другие строки подключения к базе).

  • В файле appsettings.Production.json будут значения необходимые для боевого окружения (он же продакшен).

Окружение определяется переменной ASPNETCORE__ENVIRONMENT или DOTNET__ENVIRONMENT.

Разработчик может использовать собственные суффиксы для файлов настроек, а Development и Production лишь наиболее частые что можно встретить.

Конфигурации могут быть организованы в виде дерева. Для доступа к вложенным секциям можно использовать символы : или __ в именах переменных окружения:

SET APP_ConnectionStrings__MyDB="Server=..."

Регистрация настроек в DI-контейнере

Рекомендуется регистрировать настройки отдельно для каждого компонента:

services.Configure<ApiSettings>(configuration); 
services.Configure<MetricsSettings>(configuration); 
services.Configure<MessagingSettings>(configuration); 

Также можно вручную извлекать настройки из конфигурации:

// Регистрация настроек
services.AddSingleton<IQueueSettings, QueueSettings>();

// Класс с настройками очереди
public class QueueSettings : IQueueSettings
{
    public QueueSettings(IConfiguration configuration)
    {
        var section = configuration.GetSection("Queue");
        section.Bind(this);
    }
    
    public string Host { get; init; }
    public string VirtualHost { get; init; }
    public ushort Port { get; init; }
    public string Username { get; init; }
    public string Password { get; init; }
}

Интерфейсы для работы с настройками

Существуют три основных интерфейса для работы с настройками:

  • IOptions<> - значения считываются только один раз и больше не обновляются.
  • IOptionsSnapshot<> - значения считываются на каждый scope, например один раз на каждый веб запрос. Этот метод считается не самым лучшим по производительности.
  • IOptionsMonitor<> - значения считываются при каждом обращении.

Это дает возможность использовать настройки в DI. При помощи IOptions<> можно получить настройки ранее зарегистрированные сразу нужных типов. IOptions работает как lazy и сами значение извлекаются только при прямом обращении.

Проверка настроек

При помощи атрибутов DataAnnotation можно проверить конфигурацию на корректность. Воспользовавшись этой опцией лучше всего принудительно выполнить валидацию при старте приложения иначе в случае ошибок конфигурации исключения будут выбрасываться только при попытке обращения к ним.

services
    .AddOptions<ExternalApiSettings>()
    .BindConfiguration("ExternalApiSettings")
    .ValidateDataAnnotations(); 

Локальные секреты

Для того чтобы избежать утечек чувствительных настроек в репозиторий git, лучше всего использовать локальные секреты. Это еще один json файл располагающийся в системе и подгружаемый как отдельный провайдер при локальной разработке. Расположение файла зависит от системы:

  • Windows: %APPDATA%\Microsoft\UserSecrets<user_secrets_id>\secrets.json

  • Mac & Linux: ~/.microsoft/usersecrets/<user_secrets_id>/secrets.json

Управляйте ими через командную строку:

> dotnet user-secrets init 
> dotnet user-secrets set "ConnectionStrings:Database" "Data Source=..." 
> dotnet user-secrets list 

Для боевой среды чаще всего используют готовые провайдеры секретов: Azure Key Vault, HashiCorp Vault или вовсе что-то разработанное самостоятельно.

Дополнительные материалы

[ back to home ]