Проверяем Конфигурацию при Запуске. Окончание
Начало
Доступ к другим сервисам внутри валидатора
Поскольку валидатор является обычным классом, зарегистрированным в DI, вы можете внедрять зависимости. Это одно из ключевых преимуществ по сравнению с аннотациями данных. Кроме того, аннотации данных не могут выражать зависимости между свойствами. В валидаторе вы можете добавить любую логику.
public class DatabaseOptionsValidator
: IValidateOptions<DatabaseOptions>
{
private readonly IHostEnvironment _env;
public DatabaseOptionsValidator(IHostEnvironment env)
=> _env = env;
public ValidateOptionsResult Validate(
string? name, DatabaseOptions opts)
{
var errors = new List<string>();
// Более строгие правила вне среды разработки
if (!_env.IsDevelopment())
{
…
}
…
}
}
Именованные конфигурации
IValidateOptions<T> поддерживает именованные конфигурации через параметр name. Это важно при регистрации нескольких экземпляров одного типа конфигурации — например, двух разных клиентов API:
public class ApiClientOptionsValidator
: IValidateOptions<ApiClientOptions>
{
public ValidateOptionsResult Validate(
string? name, ApiClientOptions opts)
{
…
// … Валидация общих свойств …
// Более строгие правила для API Payments
if (name == "Payments"
&& opts.Timeout > 30)
errors.Add($"Таймаут клиента [{name}] не должен превышать 30 сек.");
…
}
}
Регистрация именованных настроек:
builder.Services
.AddSingleton<IValidateOptions<ApiClientOptions>, ApiClientOptionsValidator>();
builder.Services
.AddOptions<ApiClientOptions>("Payments")
.BindConfiguration("ApiClients:Payments")
.ValidateOnStart();
builder.Services
.AddOptions<ApiClientOptions>("Shipping")
.BindConfiguration("ApiClients:Shipping")
.ValidateOnStart();
Использование в сочетании с OptionsBuilder<T>
API OptionsBuilder<T> (возвращаемый функцией AddOptions<T>()) имеет перегрузку метода .Validate(), которая принимает делегат. Это подходит для простых случаев. Для чего-либо более сложного предпочтительнее использовать реализацию IValidateOptions<T> — она позволяет тестировать логику проверки и не размещать её в Program.cs:
// Делегат — для простых проверок
builder.Services
.AddOptions<FeatureFlagOptions>()
.BindConfiguration("FeatureFlags")
.Validate(opts => opts.Percentage is >= 0 and <= 100,
"Percentage должен быть от 0 до 100")
.ValidateOnStart();
// Класс – для сложных проверок
builder.Services
.AddSingleton<IValidateOptions<SmtpOptions>, SmtpOptionsValidator>()
.AddOptions<SmtpOptions>()
.BindConfiguration("Smtp")
.ValidateOnStart();
Вы можете объединить оба варианта в одном типе параметров — фреймворк запускает все зарегистрированные валидаторы и агрегирует ошибки.
Источник: https://bartwullems.blogspot.com/2026/03/validating-configuration-at-startup.html