Skip to main content
  1. Posts/

Validating .NET Configuration

·703 words·4 mins
Chris Ayers
Author
Chris Ayers
I am a father, nerd, gamer, and speaker.
C# Advent

This blog was posted as part of the C# Advent Calendar 2022. I really want to thank Matthew D. Groves and Calvin Allen for helping set this up! Look for #csadvent on Twitter! Make sure to check out everyone else’s work when you’re done here

One of the great things about the configuration system in .NET is the type safety, dependency injection, and model binding. Something we can take advantage of is to validate our configuration on startup and fail if it doesn’t pass validation. Having that fast failure is awesome when working with containers and applications that have liveness and readiness probes.

I have some examples of the amazing configuration system in my GitHub repository I use for my .NET Configuration in Depth conference talk. Feel free to take a look at the samples found there.

But let’s get to the topic at hand. How do we validate our configuration in .NET?

Let’s start with some configuration we’ll be wanting to validate and actually fail on startup if its not there.

 "WebHook": {
    "WebhookUrl": "http://example.com/event",
    "DisplayName": "DevOps",
    "Enabled": true
  }

And let’s have a strongly typed class to hold that configuration.

using System.ComponentModel.DataAnnotations;

public class WebHookSettings
{
    public string WebhookUrl { get; set; }
    public string DisplayName { get; set; }
    public bool Enabled { get; set; }
}

Now how do we KNOW our URL is valid? We can add some validation to our application pretty easily leveraging Data Annotations! This lets us mark fields as required as well as doing other validation. There are a ton of build in attributes, like StringLength, Range, EmailAddress, and more. You can find some of them listed in the documentation if you’re curious.

Let’s extend our class to add that basic validation.

using System.ComponentModel.DataAnnotations;

public class WebHookSettings
{
    [Required, Url]
    public string WebhookUrl { get; set; }
    [Required]
    public string DisplayName { get; set; }
    public bool Enabled { get; set; }
}

For my example, I’m using minimal APIs, and I can enable validation using just a few lines of code to invoke a few extension methods.

using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOptions<WebHookSettings>()
    .BindConfiguration("WebHook")
    .ValidateDataAnnotations()
    .ValidateOnStart();

This will not only validate the DataAnnotations on my configuration class, it will also run that validation on startup.

We can see what happens if I mangle my configuration and try to start my application. With this configuration below…

"WebHook": {
    "WebhookUrl": "",
    "DisplayName": "DevOps",
    "Enabled": true
  }

Running the application fails with this error.

Unhandled exception. Microsoft.Extensions.Options.OptionsValidationException: DataAnnotation validation failed for ‘WebHookSettings’ members: ‘WebhookUrl’ with the error: ‘The WebhookUrl field is required.’.

Now its complaining about the field being blank while required. What if we populate the URL field with a bad value that isn’t a Url?

  "WebHook": {
    "WebhookUrl": "BADVALUE",
    "DisplayName": "DevOps",
    "Enabled": true
  }

We we run the app with this value, we get a different validation error.

Unhandled exception. Microsoft.Extensions.Options.OptionsValidationException: DataAnnotation validation failed for ‘WebHookSettings’ members: ‘WebhookUrl’ with the error: ‘The WebhookUrl field is not a valid fully-qualified HTTP, HTTPS, or FTP URL.’.

We can take this a step further, with additional custom validation if we choose. Let’s reset our configuration to something reasonable, but insecure..

 "WebHook": {
    "WebhookUrl": "http://example.com/event",
    "DisplayName": "DevOps",
    "Enabled": true
  }

How do we validate we’re always using https? Let’s add custom validation to our application.


builder.Services.AddOptions<WebHookSettings>()
    .BindConfiguration("WebHook")
    .ValidateDataAnnotations()
    .Validate(webHookSettings =>
        {
            return webHookSettings.WebhookUrl.StartsWith("https://");
        }, "WebHookUrl must start with https://")
    .ValidateOnStart();

Now we can validate the normal DataAnnotations as well as any custom validation logic we would like to include on startup!

Unhandled exception. Microsoft.Extensions.Options.OptionsValidationException: WebHookUrl must start with https://

If we fix everything with our validation, like so…

  "WebHook": {
    "WebhookUrl": "https://example.com/event",
    "DisplayName": "DevOps",
    "Enabled": true
  }

The application starts successfully!

$ dotnet run
Building...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7090
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5268
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

As you build your applications, think about adding validation to ensure your applications have a valid configuration at startup. Having runtime errors due to configuration is an annoying and sometimes hard problem to troubleshoot.

Please dive into the amazing documentation on the .NET Configuration system and look into the Options Pattern documentation as well!

Related

Clearing NuGet Caches

·680 words·4 mins
What is NuGet? # NuGet is an essential packaging tool used in the .NET ecosystem. NuGet is how packages for .NET are created, hosted, and consumed, along with the tools for each of those roles. For many, NuGet is used through Visual Studio to install and manage packages. The dotnet CLI also provides functionality for adding packages, updating packages, and creating packages.

Presenting Best Practices - Part 1

·940 words·5 mins
Presenting and speaking are skills that require practice to hone. I was a consultant for many years presenting to clients and customers of all levels and sizes. In addition, I started speaking and presenting at meetups, user groups, and conferences. Over the years, I practiced, I read, and I gave a lot of presentations. I’d like to share some of the learnings and best practices I’ve found in that time. I plan multiple posts, starting with Preparation. I’ll have more on slide design, and presentation tips.

Shared Focus - Using The First Way with DevOps

·366 words·2 mins
A common issue I see when discussing DevOps with teams or organizations is the presence of Organizational Silos. Organizational Silos are made up of all types of people. Sometimes its a job type, like developers, qa, or infrastructure. Sometimes its a department, like accounting, or hr. Whatever the composition of these silos, they usually impact organizational performance and the ability to deliver value to end users. This happens over time, with members of the silo identifying with each other, viewing those not in the silos as outsiders. Depending on the business, the silos can lose trust in the business overall and tighten ranks around their silo. The silos can turn into walled fortresses. When the silos get in the way, the silos are more focused on their own success than the success of the organization.

Some Tools to Help Present Git

·416 words·2 mins
I’m presenting soon on Advanced Git. I feel a lot of Developers and DevOps engineers know enough git to the job, but sometimes that’s it. I want to help people be more comfortable with the git command-line, and help alleviate some fear or hesitation in dealing with git edge cases. While researching things, I came across a few neat tools I’m using to help describe things.

Dependency Injection, Architecture, and Testing

This blog was posted as part of the Third Annual C# Advent. Make sure to check out everyone else’s work when you’re done here Dependency Injection, or DI, is a Software Architecture Design Pattern. DI is something that comes up during discussions on SOLID, IoC (Inversion of Control), testing, and refactoring. I want to speak on each of these briefly because DI touches all of these. But before I really dive into things, I want to define what a dependency is. A dependency is any object that another object requires. So all of those classes, services, and libraries that we use to build our applications are dependencies.