Skip to content

Web Hosting

ASP.NET applications often end up with a bloated Program.cs where every cross-cutting concern — authentication, CORS, Swagger, logging, health checks — is mixed together. Shiny Web Hosting lets you break that apart into modular IInfrastructureModule classes, each responsible for its own service registration and middleware configuration. Modules are discovered automatically, keeping your startup clean.

  • GitHub stars for shinyorg/extensions
  • Modular IInfrastructureModule interface — combines service registration and middleware configuration in one class
  • Automatic assembly scanning to discover and run all modules
  • Explicit registration option for reflection-free, AOT-compatible setups
  • Clean separation of concerns — one module per infrastructure component
  1. Install the NuGet package:

    Terminal window
    dotnet add package Shiny.Extensions.WebHosting
  2. Create infrastructure modules for each concern:

    using Shiny.Extensions.WebHosting;
    public class SwaggerModule : IInfrastructureModule
    {
    public void Add(WebApplicationBuilder builder)
    {
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    }
    public void Use(WebApplication app)
    {
    if (app.Environment.IsDevelopment())
    {
    app.UseSwagger();
    app.UseSwaggerUI();
    }
    }
    }
  3. Wire up modules in Program.cs:

    var builder = WebApplication.CreateBuilder(args);
    builder.AddInfrastructure(Assembly.GetExecutingAssembly());
    var app = builder.Build();
    app.UseInfrastructure();
    app.Run();

Each module implements IInfrastructureModule with two methods:

  • Add(WebApplicationBuilder builder) — register services, configuration, and anything that happens before Build()
  • Use(WebApplication app) — configure middleware, endpoints, and anything that happens after Build()
public class CorsModule : IInfrastructureModule
{
public void Add(WebApplicationBuilder builder)
{
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
});
}
public void Use(WebApplication app)
{
app.UseCors();
}
}
public class AuthModule : IInfrastructureModule
{
public void Add(WebApplicationBuilder builder)
{
builder.Services.AddAuthentication()
.AddJwtBearer();
builder.Services.AddAuthorization();
}
public void Use(WebApplication app)
{
app.UseAuthentication();
app.UseAuthorization();
}
}
public class HealthCheckModule : IInfrastructureModule
{
public void Add(WebApplicationBuilder builder)
{
builder.Services.AddHealthChecks();
}
public void Use(WebApplication app)
{
app.MapHealthChecks("/health");
}
}

Automatically discovers all IInfrastructureModule implementations in the specified assemblies:

// Scan specific assemblies
builder.AddInfrastructure(Assembly.GetExecutingAssembly());
// Scan all loaded assemblies in the AppDomain
builder.AddInfrastructureForAppDomain();

For AOT-compatible setups or when you want full control over which modules run:

builder.AddInfrastructureModules(
new SwaggerModule(),
new CorsModule(),
new AuthModule(),
new HealthCheckModule()
);

The end result is a minimal Program.cs:

using System.Reflection;
using Shiny.Extensions.WebHosting;
var builder = WebApplication.CreateBuilder(args);
builder.AddInfrastructure(Assembly.GetExecutingAssembly());
var app = builder.Build();
app.UseInfrastructure();
app.Run();

All the complexity lives in focused, testable module classes instead of a single monolithic startup file.