Category Archives: ASP.NET

Validating configuration on startup in ASP.Net core 2.2

The ability to validate configurations in Startup.cs was pushed from .NET Core 2.2 to 3.0. In the mean time I still needed to validate our configurations when they were loaded to ensure all transforms performed correctly and no configuration fields were missing. I found a few solutions online for this, but they involved middleware like Andrew Lock’s solution or using the services.Configure pattern like Vidar Kongsli’s solution. I needed something simpler and easier for developers to implement.

Configuring services in Startup.cs looked like

var cognitoConfiguration = configuration.GetSection("Aws-Cognito").Get<AwsCognitoConfiguration>();
           services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
   .AddJwtBearer(options =>
   {
      options.Audience = cognitoConfiguration.UserPoolClientId;
      options.Authority = cognitoConfiguration.AuthorityUrl.ToString();
   });

But issues can arise with this approach, for instance if UserPoolClientId isn’t populated, or if the entire 'Aws-Cognito' section was missing from the appsettings.json. A simple approach is to create an extension method for the IConfiguration class:

public static class ConfigurationExtensionMethods
{
     public static T GetSectionAndValidate<T>(this IConfiguration configuration, string sectionName)
         where T : DriveConfiguration
     {
         var results = configuration.GetSection(sectionName).Get<T>();

         results.CheckConfigurationValid();

         return results;
     }
 }

Modify all of the configuration POCO’s to use an abstract class for the validation:

public abstract class DriveConfiguration
{
    public void CheckConfigurationValid()
    {
        var results = new List<ValidationResult>();

        var isValid = Validator.TryValidateObject(
            this,
            new ValidationContext(this),
            results,
            true);

        if (!isValid)
        {
            var errorMessage = $"Missing configuration for {this.GetType().Name}: ";

            errorMessage += string
                .Join(", ", results
                    .Select(x => x.ErrorMessage));

            throw new ArgumentException(errorMessage);
        }
    }
}

A configuration POCO then uses theSystem.ComponentModel.DataAnnotations namespace to add validation attributes like [Required] or [EmailAddress]:

public class AwsCognitoConfiguration : DriveConfiguration
{
   [Required]
   public string UserPoolClientId { get; set; }

   [Required]
   public Uri AuthorityUrl { get; set; }
}

in Startup.cs getting the configuration changes to look like:

var cognitoConfiguration = _configuration.GetSectionAndValidate<AwsCognitoConfiguration>("Aws-Cognito");

When the GetSectionAndValidate<T>() executes, it populates the configuration POCO from appsettings.json and then fires off the validation. Aside from using the extension method, nothing else needs to be done in order for all configurations in Startup.cs to be validated automatically! Exceptions look like

System.ArgumentException: 'Missing configuration for CognitoConfiguration: The UserPoolClientId is required'

As you can see, it’s very easy to see what value is missing from what part of appsettings.json. This validation can be used for a health check, or even cause launch failures that an ECS cluster can respond to.

.Net Core 2.2 comes with a good base set of validators, but writing your own validator attributes is as easy as implementing ValidationAttribute:

[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class AwsAccessKeyValidatorAttribute : ValidationAttribute
{
    public AwsAccessKeyValidatorAttribute()
        : base("The {0} field must be a valid AWS Access Key")
    {
    }

    public override bool IsValid(object value)
    {
        return value is string && value.ToString().Trim().Length == 20;
    }
}

Visual Studio 2013 Settings.settings and Web.config sync Issue

.Net projects can include settings that allow the runtime of an application to be altered based on values present in the Web.config file for the application. Modifying these values doesn’t require the application to be re-compiled when they are changed, which is advantageous in production systems where settings need to be altered without disrupting the environment.

Moving a project from TFS 2008 to TFS 2013 caused some issues with syncing the Web.config with the Settings.settings file. Changes made to the Web.config and then saved usually persist back to the settings file automagically. With a deployed application modifying the Web.config settings was not affecting the application. When the project was deployed the Settings.settings file had a value that wasn’t the same as the Web.config. Strangely, after deploying the application the application ignored changes made to the Web.config.

After a few minutes of head scratching I peeked into the Settings.Designer.cs file and found the setting was not updating when changed in the Web.config. I manually changed the string returned by the setting and saved the file. Like magic, the Web.config started updating the Settings.setting.

Capture

[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("test")]
public string Setting {
   get {
      return ((string)(this["Setting"]));
   }
}

Capture

Capture