【问题标题】:How to validate config values from appSettings.json file automatically during DI setup?如何在 DI 设置期间自动验证 appSettings.json 文件中的配置值?
【发布时间】:2023-04-01 07:07:01
【问题描述】:

我在 .NET Core 项目的 appSettings.json 文件中添加了配置。为了简单起见,我以数据库设置为例。所以在设置文件中你会有

{
  "Database": {
    "Host": "localhost",
    "Port": 1234,
    "Database": "myDb",
    "Username": "username",
    "Password": "pw",
    "EnablePooling": true
  }
}

在 Startup.cs 文件中配置服务时,我希望通过依赖注入来访问这些设置。这个的数据模型是

public class DatabaseSettings
{
    public string Host { get; set; }
    public ushort Port { get; set; }
    public string Database { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public bool EnablePooling { get; set; }
}

我是这样配置的

private void SetupSettings(IServiceCollection services)
{
    ServiceProvider serviceProvider = services.BuildServiceProvider();
    IConfiguration configuration = serviceProvider.GetService<IConfiguration>();

    IConfigurationSection databaseConfigurationSection = configuration.GetSection("Database");
    services.Configure<DatabaseSettings>(databaseConfigurationSection);
}

最后,我想验证这些设置。我知道我可以创建一个实现IValidateOptions 接口的验证器类。

public class DatabaseSettingsValidator : IValidateOptions<DatabaseSettings>
{
    private readonly IList<string> failures;

    public DatabaseSettingsValidator()
    {
        failures = new List<string>();
    }
    
    public ValidateOptionsResult Validate(string databaseSettingsName, DatabaseSettings databaseSettings)
    {
        if (databaseSettings == null)
            failures.Add($"{databaseSettingsName} are required.");
        
        if (string.IsNullOrEmpty(databaseSettings?.Host))
            failures.Add($"{nameof(databaseSettings.Host)} must not be empty.");
        
        if (string.IsNullOrEmpty(databaseSettings?.Database))
            failures.Add($"{nameof(databaseSettings.Database)} must not be empty.");
        
        if (failures.Any())
            return ValidateOptionsResult.Fail(failures);

        return ValidateOptionsResult.Success;
    }
}

但是我必须自己创建这个类并调用Validate 方法吗?也许有类似这个示例代码的东西?

.

services.ValidateConfiguration<IOptions<DatabaseSettings>, DatabaseSettingsValidator>();

所以你传入配置的设置和要使用的验证器。

【问题讨论】:

    标签: c# asp.net-core .net-core asp.net-core-mvc


    【解决方案1】:

    但我正在努力解决两个问题:

    有没有一种方法可以收集所有失败而不是在之后返回 一?所以你会得到一个失败的列表,而不是修复一个 减一。

    我必须自己创建这个类并调用 Validate 方法吗? 也许有类似这个示例代码的东西?

    services.ValidateConfiguration();所以你传入配置的设置 以及要使用的验证器。

    是的,我们可以收集所有失败列表并立即显示它们,我们还可以创建一个包含 Validate 方法的类。请检查以下步骤:

    首先,由于类名是“DatabaseSettings”,因此最好将配置节名称设置为与类名相同:

    {
      "DatabaseSettings": {
        "Host": "localhost",
        "Port": 1234,
        "Database": "myDb",
        "Username": "username",
        "Password": "pw",
        "EnablePooling": true
      }
    }
    

    [注意]如果使用不同的名称,该值可能不会映射到Database Setting类,因此在验证数据时,它们都为空。

    其次,使用 Data Annotations 方法将验证规则添加到模型属性中。

    public class DatabaseSettings
    {
        [Required]
        public string Host { get; set; }
        [Required]
        public ushort Port { get; set; }
        [Required]
        public string Database { get; set; }
        [Required]
        public string Username { get; set; }
        [Required]
        public string Password { get; set; }
        [Required]
        public bool EnablePooling { get; set; }
    }
    

    第三,创建一个包含 ConfigureAndValidate 方法的 ServiceCollectionExtensions 类:

    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection ConfigureAndValidate<T>(this IServiceCollection @this,
            IConfiguration config) where T : class
            => @this
                .Configure<T>(config.GetSection(typeof(T).Name))
                .PostConfigure<T>(settings =>
                {
                    var configErrors = settings.ValidationErrors().ToArray();
                    if (configErrors.Any())
                    {
                        var aggrErrors = string.Join(",", configErrors);
                        var count = configErrors.Length;
                        var configType = typeof(T).Name;
                        throw new ApplicationException(
                            $"Found {count} configuration error(s) in {configType}: {aggrErrors}");
                    }
                });
    }
    

    然后,注册 ConfigureAndValidate 服务:

    public void ConfigureServices(IServiceCollection services)
    {
        services.ConfigureAndValidate<DatabaseSettings>(Configuration);
    }
    

    最后,获取 Exception 列表。

    public class HomeController : Controller
    {
        private readonly DatabaseSettings_settings;
    
        public HomeController(IOptions<DatabaseSettings> settings)
        {
            _settings = settings.Value; // <-- FAIL HERE THROW EXCEPTION
        }
    }
    

    然后,测试结果是这样的(我从 appSettings.json 中删除了主机和用户名):

    更多详细信息,可以查看这个博客:Validating configuration in ASP.NET Core

    【讨论】:

    • 这看起来真的有点矫枉过正,为什么要实现IValidateOptions接口然后=?
    • 我认为这家伙用较小的努力解决了它stackoverflow.com/questions/57135724/…
    • 但是为您的解决方案 +1 :)
    【解决方案2】:

    ValidateOptions主要针对复杂的场景,使用ValidateOptions的目的是可以将验证逻辑移出启动。

    我认为对于您的场景,您可以使用以下代码作为参考

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOptions<MyConfigOptions>()
            .Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
            .ValidateDataAnnotations()
            .Validate(config =>
            {
                if (config.Key2 != 0)
                {
                    return config.Key3 > config.Key2;
                }
    
                return true;
            }, "Key3 must be > than Key2.");   // Failure message.
    
        services.AddControllersWithViews();
    }
    

    更多详情,请参考此文档 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1#options-validation

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-23
      • 2021-04-24
      • 2021-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多