Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. 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. The data model for this is

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.

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? 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.

like image 696
Question3r Avatar asked Aug 11 '20 20:08

Question3r


People also ask

How do I run Appsettings json at startup?

In order to add AppSettings. json file, right click on the Project in Solution Explorer. Then click Add, then New Item and then choose App Settings File option (shown below) and click Add button. Once the File is created, it will have a DefaultConnection, below that a new AppSettings entry is added.

How do I get value from IConfiguration?

Using IConfiguration The IConfiguration is available in the dependency injection (DI) container, so you can directly access JSON properties by simply injecting IConfiguration in the constructor of a controller or class. It represents a set of key/value application configuration properties.

What is the use of IConfiguration?

The IConfiguration is an interface for . Net Core 2.0. The IConfiguration interface need to be injected as dependency in the Controller and then later used throughout the Controller. The IConfiguration interface is used to read Settings and Connection Strings from AppSettings.


1 Answers

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? Maybe there is something like this sample code?

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. 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:

{
  "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.

Second, using the Data Annotations method adds validation rules to the model properties.

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:

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:

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):

enter image description here

More detail information, you can check this blog:Validating configuration in ASP.NET Core

like image 108
Zhi Lv Avatar answered Oct 20 '22 08:10

Zhi Lv