Environment specific startup Configure methods

How environment specific named Configure methods can be used during startup

Home DailyDrop

Daily Knowledge Drop

When using the Startup class approach for application startup (vs the top level minimal api approach introduced in .NET6), instead of the normal Configure method used to configure the middleware pipeline, a method can be specified per environment. This environment specific Configure method is automatically invoked depending on the ASPNETCORE_ENVIRONMENT environment variable value.


Startup

Prior to .NET 6, when creating an ASP.NET Core web API project, a Program.cs file is created containing the Main method, and a separate Startup.cs file is created containing the dependency injection setup method (ConfigureServices) as well as the middleware pipeline setup method Configure.

The Host setup in Program.cs is configured to use the Startup class in Startup.cs to get its configuration:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            // Use the Startup class
            webBuilder.UseStartup<Startup>();
        });

In .NET6 the default changes to contain only a Program.cs, and instead of a instance of HostBuilder being used, an instance of WebApplicationBuilder is used:

var builder = WebApplication.CreateBuilder(args);

The .NET6 default can be retro-fitted to use the same approach as prior versions (Startup class), but this post is mainly focused on versions prior to .NET6.


Default Configuration

By default in the Startup.cs the Configure method which is used to setup the middleware pipeline. This method is automatically invoked by the runtime:

// This method gets called by the runtime. Use this method to 
// configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

If the pipeline needs to differ per environment, then checks, such as the one below, need to be implemented:

// Check if we are running in a development environment
if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

Here the developer exception page is only configured if the application is running in Development - when the ASPNETCORE_ENVIRONMENT is set to Development.

Using these checks is the default technique to configure the pipeline per environment - however, especially if the pipelines differ greatly per environment, these checks could become complicated and potentially confusing.

An alternative technique is to setup a configure method per environment.


Environment Configuration

Setting up a Configure method per environment is very simple - in the Startup class, just create a new method called Configure.

For example, the ConfigureTest method will be called when running in Test, instead of the default Configure method, and the ConfigureProduction method will be called when running in Production:


public void ConfigureTest(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async (HttpContext ctx) => 
            await ctx.Response.WriteAsync("In Test environment"));
        endpoints.MapControllers();
    });
}

public void ConfigureProduction(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

It no environment specific method could be found, then the default Configure method is called.

The same process can be applied to "non-built-in" environments (Development, Test, Production) - if when running locally, the ASPNETCORE_ENVIRONMENT environment variable is set to LocalDevelopment, for example, then the following method will be called:

// Method named Configure{EnvironmentName}
 public void ConfigureLocalDevelopment(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/debug", async (HttpContext ctx) => 
            await ctx.Response.WriteAsync("Special debug information"));
        endpoints.MapControllers();

    });
}

Notes

Perhaps not especially relevent if working with the latest version of .NET (6 and above), or if there is not major differences in middleware pipelines between difference environments - this is still an interesting technique available if one requires it to ensure cleaner and easier to understand code.


Daily Drop 123: 25-07-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net api configuration