You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 7中如何用FluentValidation在启动和变更时验证Options

.NET 7 Web API中使用FluentValidation实现Options启动时验证

问题描述

我正在使用.NET 7创建Web API项目,希望通过FluentValidation配置并验证Options。现有LoggingOptions和对应的验证器代码如下:

public sealed class LoggingOptions
{
    public const string SectionName = "Logging";
    
    public LogLevel LogLevel { get; init; } = LogLevel.Information;
}

public sealed class LoggingOptionsValidator : AbstractValidator<LoggingOptions>
{
    public LoggingOptionsValidator()
    {
        RuleFor(options => options.LogLevel).IsInEnum();
    }
}

当前Program.cs代码能在Options变更时验证,但启动时不会验证配置:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<LoggingOptions>(builder.Configuration.GetSection(LoggingOptions.SectionName));
builder.Services.AddTransient<IValidator<LoggingOptions>, LoggingOptionsValidator>();

builder.Services.AddControllers();

var app = builder.Build();

app.Services.GetRequiredService<IOptionsMonitor<LoggingOptions>>().OnChange(options =>
{
     var validator = app.Services.GetRequiredService<IValidator<LoggingOptions>>();
     var validationResult = validator.Validate(options);

     if (!validationResult.IsValid)
     {
         var validationErrors = validationResult.Errors.Select(error => error.ErrorMessage);
         throw new OptionsValidationException(nameof(LoggingOptions), typeof(LoggingOptions), validationErrors);
     }
});

app.UseHttpsRedirection();
app.MapControllers();
app.Run();

尝试用ValidateOnStart()实现启动时验证,但不知道如何在验证逻辑中获取IValidator<LoggingOptions>实例:

builder
    .Services
    .AddOptions<LoggingOptions>()
    .Validate(options =>
    {
        var validator = ???; // 如何获取验证器?
        var validationResult = validator.Validate(options);

        if (!validationResult.IsValid)
        {
            var validationErrors = validationResult.Errors.Select(error => error.ErrorMessage);
            throw new OptionsValidationException(nameof(LoggingOptions), typeof(LoggingOptions), validationErrors);
        }
        
        return true;
    })
    .ValidateOnStart()
    .PostConfigure(options =>
    {
        var validator = ???; // 如何获取验证器?
        var validationResult = validator.Validate(options);

        if (!validationResult.IsValid)
        {
            var validationErrors = validationResult.Errors.Select(error => error.ErrorMessage);
            throw new OptionsValidationException(nameof(LoggingOptions), typeof(LoggingOptions), validationErrors);
        }
    });

请问如何正确实现Options在启动时的验证?


解决方案

方法1:使用FluentValidation扩展方法(推荐)

FluentValidation提供了针对Options的原生扩展,无需手动解析验证器,直接整合到配置流程中。

首先安装依赖包:

dotnet add package FluentValidation.DependencyInjectionExtensions

修改Program.cs,整合启动时验证与变更时验证:

var builder = WebApplication.CreateBuilder(args);

// 自动注册程序集中的所有验证器
builder.Services.AddValidatorsFromAssemblyContaining<LoggingOptionsValidator>();

// 配置Options并启用双阶段验证
builder.Services.AddOptions<LoggingOptions>()
    .Bind(builder.Configuration.GetSection(LoggingOptions.SectionName))
    // 注入验证器进行验证,失败抛出标准OptionsValidationException
    .Validate<IValidator<LoggingOptions>>((options, validator) =>
    {
        var result = validator.Validate(options);
        if (!result.IsValid)
        {
            throw new OptionsValidationException(
                nameof(LoggingOptions), 
                typeof(LoggingOptions), 
                result.Errors.Select(e => e.ErrorMessage)
            );
        }
        return true;
    })
    .ValidateOnStart(); // 启用启动时验证

builder.Services.AddControllers();

var app = builder.Build();

// 保留配置变更时的验证逻辑
app.Services.GetRequiredService<IOptionsMonitor<LoggingOptions>>().OnChange(options =>
{
    var validator = app.Services.GetRequiredService<IValidator<LoggingOptions>>();
    var validationResult = validator.Validate(options);

    if (!validationResult.IsValid)
    {
        var validationErrors = validationResult.Errors.Select(error => error.ErrorMessage);
        throw new OptionsValidationException(nameof(LoggingOptions), typeof(LoggingOptions), validationErrors);
    }
});

app.UseHttpsRedirection();
app.MapControllers();
app.Run();

方法2:手动通过服务提供者解析验证器

如果不想用扩展方法,可以利用PostConfigure的服务注入能力,直接获取验证器:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<LoggingOptions>(builder.Configuration.GetSection(LoggingOptions.SectionName));
builder.Services.AddTransient<IValidator<LoggingOptions>, LoggingOptionsValidator>();

// 配置启动时验证
builder.Services.AddOptions<LoggingOptions>()
    .ValidateOnStart()
    // PostConfigure支持注入IServiceProvider
    .PostConfigure((options, serviceProvider) =>
    {
        var validator = serviceProvider.GetRequiredService<IValidator<LoggingOptions>>();
        var result = validator.Validate(options);
        
        if (!result.IsValid)
        {
            throw new OptionsValidationException(
                nameof(LoggingOptions), 
                typeof(LoggingOptions), 
                result.Errors.Select(e => e.ErrorMessage)
            );
        }
    });

builder.Services.AddControllers();

var app = builder.Build();

// 变更时验证逻辑不变
app.Services.GetRequiredService<IOptionsMonitor<LoggingOptions>>().OnChange(options =>
{
    var validator = app.Services.GetRequiredService<IValidator<LoggingOptions>>();
    var validationResult = validator.Validate(options);

    if (!validationResult.IsValid)
    {
        var validationErrors = validationResult.Errors.Select(error => error.ErrorMessage);
        throw new OptionsValidationException(nameof(LoggingOptions), typeof(LoggingOptions), validationErrors);
    }
});

app.UseHttpsRedirection();
app.MapControllers();
app.Run();

核心要点

  • Validate<IValidator<T>>是最简洁的方式,直接将验证器注入到验证逻辑中,避免手动解析服务。
  • ValidateOnStart()会在应用启动阶段立即验证配置,验证失败直接终止启动,防止应用带着无效配置运行。
  • 启动时验证与变更时验证是独立的,ValidateOnStart()不覆盖配置变更后的验证场景,仍需通过IOptionsMonitor.OnChange监听处理。

内容的提问来源于stack exchange,提问作者baitendbidz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 18:24:53