Validate appsettings in ASP.net Core using FluentValidation

Introduction

In the ASP.net core all the application configurations are maintained in appsettings.json. The appsettings.json contains many settings like database connections strings, logging settings, and authentication settings etc. Applications settings are injected into the application as IConfiguration. Application uses these settings to perform certain tasks. For example establishing a connection to database. What happens when you update a wrong database connection string settings in your appsettings.json file? Shouldn’t we validate the settings before establishing a connection to database? This article shows you how to validate the appsettings.json while application starts.

Why should we validate appsettings.json?

There are some benefits validating appsettings.json,

·       Centrally manage the validations

Applications settings are consumed in many parts  of the application. The validations of these settings are scattered across the code. Instead we can centrally manage those validations at one place.

·       Validate before application starts

It is the best practice to validate the application setting even before the application starts. We don’t have to wait until the application finds that there is an issue with the settings deep inside the somewhere in our code flow.

Validate appsettings.json using FluentValidation

FluentValidation is a .NET library for building strongly-typed validation rules. It uses a fluent interface and lambda expressions for building validation rules. It helps clean up your domain code and make it more cohesive, as well as giving you a single place to look for validation logic.

Predominantly FluentValdiation is used to validate API input request model. We use FluentValidation for validating appsettings.json as well.

More on FluentValidation can be read at https://fluentvalidation.net/

Figure 1 Components of Validation

Let us assume that we have to add few authorization settings in appsettings.json. Below is the model for ‘AuthSettings’,

  public class AuthSettings

    {

        public AuthSettings(){}

 

        public string Provider { get; set; }

        public string Secret { get; set; }

        public string Issuer { get; set; }

        public string Audience { get; set; }

        public int ExpirationMinutes { get; set; }

    }

Here is how the AuthSettings will look like in our appsettings.json,

"AuthSettings": {

    "Provider": "AzureAD",

    "Secret": "Some Secret",

    "Issuer": "http://someauthorizationurl.com",

    "Audience": "http:// someauthorizationurl.com",

  }

 The validator for ‘AuthSettings’ is follows,

public class AuthSettingsValidator : AbstractValidator<AuthSettings>

    {

        public AuthSettingsValidator()

        {

            When(authsettings => authsettings != null, () =>

            {

                RuleFor(x => x.Secret)

                .NotEmpty();

 

                RuleFor(x => x.Issuer)

                .NotEmpty();

 

                RuleFor(x => x.Audience)

               .NotEmpty();

 

                RuleFor(x => x.ExpirationMinutes)

               .NotEmpty();

            });

        }

    }

Our ‘AuthSettingsValidator’ uses fluent validation and validates ‘AuthSettings’ model.

Below is the generic method for validation,

      private static void Validate<T, U>(string section, IConfiguration configuration) where T : class where U : new()

        {

            var settings = configuration.GetSection(section).Get<T>();

            if (settings == null)

            {

                throw new Application.Exceptions.ValidationException("Configuration", $"{nameof(section)} not found");

            }

            var validator = (IValidator<T>)new U();

            var validationResult = validator.Validate(settings);

 

            if (validationResult.Errors.Count > 0)

            {

                throw new Application.Exceptions.ValidationException(validationResult);

            }

        }

The generic validation method take two template T and U, where T is the model to be validated and U is the validator class for T.

The method creates a validator object and invokes Validate() method,

var validator = (IValidator<T>)new U();

var validationResult = validator.Validate(settings);

If there is any validation error, the method raises the ‘ValidationException’.

The below extension method shows how to use the generic validate method to validate a specific section in out appsettings.json,

        public static IHostBuilder UseConfigValidation(this IHostBuilder hostbuilder, IConfiguration configuration)

        {

            Validate<AuthSettings, AuthSettingsValidator>("AuthSettings", configuration);

            return hostbuilder;

        }

 

The method is called from the host builder as follows,

public static IHostBuilder CreateHostBuilder(string[] args, IConfiguration config) =>

            Host.CreateDefaultBuilder(args)

                .UseConfigValidation(config)

                .ConfigureWebHostDefaults(webBuilder =>

                {

                    webBuilder.UseStartup<Startup>();

                });

 

The below screen grab shows when ‘ValidationException‘ was thrown,

Figure 2 Screen grab shows when ValidationExcption was thrown

 

Summary

The source code for the above can be downloaded at this github url, https://github.com/arunvambur/ValidateAppsettings

Comments

Popular posts from this blog

Debugging and Testing Helm Charts Using VS Code

Handle Multipart Contents in Asp.Net Core