ASP.NET Core 2.2 – Options Validation Exploring the new validation feature when using the options pattern in ASP.NET Core.

I was listening to this week’s ASP.NET Community Standup and during the introduction, Jon Galloway shared Andrew Lock’s blog post, “Delaying strongly-typed options configuration using PostConfigure in ASP.NET Core“. It sparked some discussion, during which Damien Edwards mentioned a new feature in ASP.NET Core 2.2 called Options Validation which I hadn’t previously explored. The functionality was added by Hao Kung, who works on the ASP.NET team, in this PR.

The idea is quite simple and I think will be well received for handling scenarios where it is important to validate the configuration for an application.

At this mention of the feature, I was inspired to take a quick look and share my experience here. I’ve created a simple ASP.NET Core 2.2 project to experiment with the feature. You can download my sample, which uses Options Validation from my GitHub repo.

Adding Validation

Options Validation can be set up in two main ways (that I’ve found so far!).

The first method is to use Data Annotations on your strongly typed options classes.

Above, we have a CustomConfig class to which will we’ll bind some application configuration values.

We’ve marked “Setting1” as required using a Data Annotation attribute.

In the ConfigureServices method, we can add the following code…

By calling the AddOptions extension method on the IServiceCollection we get an OptionsBuilder for the CustomConfig type.

Using this builder, we can first bind the appropriate IConfiguration section to the CustomConfig option. This will bind the values loaded into the configuration for the application to matching properties on the CustomConfig class.

The call to ValidateDataAnnotations sets up validation using any suitable data annotations.

After this, we call the Validate method, which is the second approach that can be used to configure validation. The Validate method accepts a Func<T, bool>, where T is the type of your strongly typed options class. You can access the properties of your typed options class, in this case, the CustomConfig class, to perform validation checks. 

If the Func returns true, the validation is deemed to have succeeded. In this example, we’ve created a rule that will cause a validation failure, in the situation where Setting2 has been set, but Setting3 has not. This is quite a common requirement for configuration, where setting one value, requires others to also be provided. As we have done in this sample, we can optionally provide an error message for a validation failure.

At this point, we have set up the validation for our Options class.

Validating Options

Currently, as of ASP.NET Core 2.2, validation occurs at first use, so this may not be ideal for all scenarios. Eager validation is being considered for the 3.0 time-frame. You can see a little discussion about that in this GitHub issue.

As an example, we’ll use the HomeController to access the option by accepting an IOption<CustomConfig> in the constructor…

As this is the first use of the option, the validation will run. Since we have not configured the required Setting1 value anywhere, validation fails as expected. In this case, an OptionsValidationException is thrown which we’ve caught. The exception contains a collection of validation failures which we loop over and log.

Running the application we see the following failure logged to the console…

fail: OptionsValidationSample.Controllers.HomeController[0]
DataAnnotation validation failed for members Setting1 with the error ‘The Setting1 field is required.’.

We get a failure for the data annotation, required property on the CustomConfig class. Note that there is no error for our second validation requirement. That’s because Setting2 has not been set, so currently, validating is passing.

Let’s now provide some configuration values which will be bound to the CustomConfig option so that we can exercise that second piece of validation.

In our appsettings.json we can add some values for the settings…

We’ve now set the required Setting1 (so that should no longer fail) and provided a value for Setting2, but not Setting3.

Running our application again we now see a different failure in the OptionsValidationException.

fail: OptionsValidationSample.Controllers.HomeController[0]
Setting 3 is required when Setting2 is present

This time we see the custom error message we provided when calling the Validate method on the OptionsBuilder.

Custom Data Annotation Errors

It is also possible to add custom error messages using the Data Annotation approach. We can update our CustomConfig as follows…

We have now set the ErrorMessage property on the data annotation.

After updating the appsettings.json to set Setting1 to null (to force a failure), running the application results in the following error in the log…

fail: OptionsValidationSample.Controllers.HomeController[0]
DataAnnotation validation failed for members Setting1 with the error ‘Custom Error Message’.

Summary

Options Validation looks like a really handy feature to solve a reasonable need to validate application configuration. It’s a shame that the eager validation is missing but it would be possible to add some custom code to handle that for the time being. It seems likely that bad config means the application probably can’t or shouldn’t start properly, so failing early would be useful.

For now, I hope this blog gets you going with the feature.