ASP.NET Core Startup Validation Process - Complete Guide

Positiwise
4 min readNov 27, 2023

--

Validation is an important part of any ASP.NET Core application. You want to validate user input on the client side with JavaScript/TypeScript and on the server side in your C# code to make sure bad or malformed data doesn’t make its way into your database or cause errors in your application.

In this post, we’ll focus specifically on validating startup configuration and service registration in ASP.NET Core. When your application starts, the Startup class’s ConfigureServices method runs and registers all your app’s services. The Configure method then sets up your request processing pipeline. It’s important to validate that things are configured correctly at startup before your app starts handling requests.

We’ll cover several approaches to validate your app on startup:

  • Using the built-in ASP.NET Core hosting infrastructure with IHostingEnvironment
  • Custom validation in Startup with ValidateOnStartup
  • Validation attributes on startup services
  • Third-party libraries like Scrutor

For each approach, we’ll see code examples and discuss the pros and cons. By the end, you’ll have several good options to implement startup validation in your ASP.NET Core web apps and APIs.

Built-in IHostingEnvironment Validation

The ASP.NET Core hosting interfaces include an IHostingEnvironment interface that provides useful startup validation capabilities out of the box.

It includes an EnvironmentName property that indicates the current environment name like "Development," "Staging," or "Production". You can check EnvironmentName in ConfigureServices or Configure and throw an exception if incorrect:

public void ConfigureServices(IServiceCollection services)
{
if (env.EnvironmentName != "Development")
{
throw new Exception("Invalid environment!");
}

// register services
}

It validates that the code is not accidently running in the wrong environment. IHostingEnvironment also provides an IsEnvironment method to check for specific environments:

if (!env.IsEnvironment("Development") 
{
// throw error
}

Pros:

  • Simple validation using built-in ASP.NET Core interfaces
  • Can validate environment name in ConfigureServices and Configure

Cons:

  • Very basic — only validates environment name
  • Still allows invalid services and configs to be registered

Overall, IHostingEnvironment provides a simple way to check for the proper environment on startup. But it doesn't allow validating other aspects of your configuration and services.

Custom Validation in Startup

For more advanced startup validation, you can create custom validation code directly in your Startup class’s ConfigureServices and Configure methods.

Here is an example:

public void ConfigureServices(IServiceCollection services)
{
// custom validation
ValidateDatabaseSettings(Configuration);

// register services
services.AddDbContext<AppDbContext>(...);
}

private void ValidateDatabaseSettings(IConfiguration config)
{
// check connection strings
var cnn = config.GetConnectionString("DefaultConnection");
if (string.IsNullOrEmpty(cnn)
{
throw new Exception("DefaultConnection missing!");
}

// check provider
if (cnn.IndexOf("SQLServer") < 0)
{
throw new Exception("Invalid DB provider!");
}
}

This allows you to call private helper methods like ValidateDatabaseSettings to validate important app configurations and settings.

You can also validate registered services directly in ConfigureServices once they are registered:

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
ValidateMvcServices(services);
}

private void ValidateMvcServices(IServiceCollection services)
{
if (!services.Any(x => x.ServiceType == typeof(Controllers.HomeController))
{
throw new Exception("Missing controller!");
}
}

This validates that essential framework services like MVC controllers are correctly registered before the app starts.

Pros:

  • Complete flexibility to validate app config and services
  • Can integrate with other validation libraries

Cons:

  • Puts large validation code blocks directly in the Startup class
  • No separation of validation code from main app code

The main downside is that the validation code ends up directly coupled to the Startup class configuration logic. That reduces the separation of concerns.

Validation Attributes on Startup Services

A cleaner approach is to create custom validation attributes that can be applied to services registered in ConfigureServices. Here is an example:

First, define a custom validation attribute:

public class StartupServiceValidator : Attribute 
{
Action<IServiceProvider> Validate;

public StartupServiceValidator(Action<IServiceProvider> validate)
{
Validate = validate;
}
}

This attribute takes an action delegate that will handle validation.

Next, apply it to startup services:

[StartupServiceValidator(ValidateDbContext)]
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(...);
}

private void ValidateDbContext(IServiceProvider provider)
{
using var scope = provider.CreateScope();
var context = scope.ServiceProvider.GetService<AppDbContext>();

if (context == null)
throw new Exception("DbContext not registered!");
}

Here the StartupServiceValidator attribute runs the ValidateDbContext method after AppDbContext is registered to validate it instantiated correctly.

Multiple validations can be defined on different methods by creating multiple StartupServiceValidator attributes.

Pros:

  • Decouples validation code from main startup logic
  • Allows fine-grained control over validations

Cons:

  • It is still somewhat cumbersome to define validations
  • It is easy to forget to add attributes during later code changes

The attribute approach keeps validations separated from primary startup logic. However, creating and remembering to add attributes during code changes can be cumbersome.

Third-Party Libraries

Finally, there are some nice third-party libraries created specifically for startup validation in ASP.NET Core. One great option is Scrutor by Khellang.

Scrutor enhances ASP.NET Core’s built-in service registration to allow registering validators to access the full IServiceProvider.

To validate with Scrutor:

  1. Install NuGet package
  2. Call Scrutor.Scan instead of services.Scan in ConfigureServices
  3. Register startup validators using Scrutor’s IStartupValidator interface

Here is an example:

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();

services.Scan(scan => scan
.FromAssemblyOf<Startup>()
.AddClasses()
.AsSelf()
.WithTransientLifetime());

services.ScrutorScan(scan => scan
.FromAssemblyOf<Startup>()
.AddClasses()
.AsSelf()
.WithTransientLifetime())
.RegisterValidators();
}

This registers Scrutor instead of the default service scanning.

Next, create validators:

public class DbContextValidator : IStartupValidator
{
public void Validate(IServiceProvider provider)
{
// validate db context
}
}

public class MvcValidator : IStartupValidator
{
public void Validate(IServiceProvider provider)
{
// validate mvc
}
}

These implement Scrutor’s IStartupValidator interface and have full access to the IServiceProvider to validate app services and configuration. Scrutor discovers them automatically on startup.

Pros:

  • Simple and discoverable validation setup
  • No attributes required

Cons:

  • Additional third-party dependency

Scrutor provides an easy yet flexible way to implement startup validation in ASP.NET Core without a lot of custom code. The main downside is adding an extra NuGet dependency.

Conclusion

Validating startup configuration is important to catch errors early before an ASP.NET Core app starts handling requests.

There are several good approaches built in or available from third parties:

  • IHostingEnvironment for basic environment checks
  • Custom validation code in Startup
  • Validation attributes on startup services
  • Third-party libraries like Scrutor

Scrutor makes it easy to create discoverable validators with full access to app services for validation without a lot of glue code.

Overall, there are good options to fit different needs and complexity levels for validating app startup in ASP.NET Core. Proper validation will catch errors early and help avoid nasty production issues down the road!

--

--

Positiwise
Positiwise

Written by Positiwise

Positiwise.com is a leading IT company, based in India, offering top-notch full-cycle software development through its dedicated team of professionals.

Responses (1)