简体   繁体   English

如何在 DI 设置期间自动验证 appSettings.json 文件中的配置值?

[英]How to validate config values from appSettings.json file automatically during DI setup?

I added configurations to the appSettings.json file in my .NET Core project.我在我的 .NET Core 项目中的 appSettings.json 文件中添加了配置。 For the sake of simplicy I'm taking database settings as an example.为了简单起见,我以数据库设置为例。 So in the settings file you would have所以在设置文件中你会有

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

When configuring the services in the Startup.cs file I want to make those settings accessible via dependency injection.在 Startup.cs 文件中配置服务时,我希望通过依赖注入来访问这些设置。 The data model for this is数据 model 是

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; }
}

and I configure it this way我这样配置

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

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

Lastly I want to validate those settings.最后我想验证这些设置。 I know that I can create a validator class implementing the IValidateOptions interface.我知道我可以创建一个验证器 class 实现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;
    }
}

but do I have to create this class and call the Validate method on my own?但是我必须创建这个 class 并自己调用Validate方法吗? Maybe there is something like this sample code?也许有类似这个示例代码的东西?

. .

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

So you pass in the configured settings and the validator to use.因此,您传入配置的设置和要使用的验证器。

but I'm struggling with two questions:但我正在努力解决两个问题:

Is there a way I can collect all failures instead of returning after one?有没有一种方法可以收集所有失败而不是在一个之后返回? So you would get a list of failures instead of having to fix one by one.所以你会得到一个失败的列表,而不是一个一个地修复。

Do I have to create this class and call the Validate method on my own?我是否必须自己创建这个 class 并调用 Validate 方法? Maybe there is something like this sample code?也许有类似这个示例代码的东西?

services.ValidateConfiguration<IOptions, DatabaseSettingsValidator>(); services.ValidateConfiguration<IOptions, DatabaseSettingsValidator>(); So you pass in the configured settings and the validator to use.因此,您传入配置的设置和要使用的验证器。

Yes, we could collect all failures list and display them at once, and we could also create a class which contains the Validate method.是的,我们可以收集所有故障列表并立即显示它们,我们还可以创建一个包含 Validate 方法的 class。 Please check the following steps:请检查以下步骤:

First, since the class name is " DatabaseSettings ", it better sets the config section name as the same as the class name:首先,由于 class 名称为“ DatabaseSettings ”,因此最好将配置节名称设置为与 class 名称相同:

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

[Note] If using a different name, the value might not map to the Database Setting class, so when validate the data, they all null. [注意] 如果使用不同的名称,则数据库设置 class 中的值可能不是 map,因此在验证数据时,它们都是 null。

Second, using the Data Annotations method adds validation rules to the model properties.其次,使用数据注释方法将验证规则添加到 model 属性。

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; }
}

Third, create a ServiceCollectionExtensions class which contains the ConfigureAndValidate method:第三,创建一个包含 ConfigureAndValidate 方法的 ServiceCollectionExtensions class:

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}");
                }
            });
}

Then, register the ConfigureAndValidate service:然后,注册 ConfigureAndValidate 服务:

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

Finally, get the Exception list.最后,获取异常列表。

public class HomeController : Controller
{
    private readonly DatabaseSettings_settings;

    public HomeController(IOptions<DatabaseSettings> settings)
    {
        _settings = settings.Value; // <-- FAIL HERE THROW EXCEPTION
    }
}

Then, test result like this (I removed the Host and Username from the appSettings.json):然后,像这样的测试结果(我从 appSettings.json 中删除了主机和用户名):

在此处输入图像描述

More detail information, you can check this blog: Validating configuration in ASP.NET Core更多详细信息,您可以查看此博客: Validating configuration in ASP.NET Core

ValidateOptions are mainly for complex scenario, the purpose of using ValidateOptions is that you can move the validate logic out of startup. ValidateOptions 主要针对复杂的场景,使用 ValidateOptions 的目的是可以将验证逻辑移出启动。

I think for your scenario, you can use below code as a reference我认为对于您的情况,您可以使用以下代码作为参考

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();
}

For more details, please refer to this document https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1#options-validation有关详细信息,请参阅此文档https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1#options-validation

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM