Enable/Disable SwaggerUI in runtime

Viewed 2510

I am having 4 environment Development, Test, Staging, and Production.

We have to disable the Swagger for the Production environment only, but this should be configurable like if we want, we can enable it as well if in case to test some endpoint without building the application again.

This enabling and disabling of Swagger should be done on a runtime basis, as I mentioned I don't want to build the application again.

Thanks in Advance.

2 Answers

I control this with configuration e.g. in appsettings.json.

{
   "applicationSettings": {
      "serviceName": "Test",
      "swaggerUIEnabled": true
   }
}

And then configure Startup.cs as follows using Swashbuckle.AspNetCore:

public class Startup
{
   private readonly IConfiguration _configuration;

   public Startup(IConfiguration configuration)
   {
      _configuration = configuration;
   }

   public void ConfigureServices(IServiceCollection services)
   {
      services.AddSettings<ApplicationSettings>(_configuration.GetSection("applicationSettings"));

      // ...

      if (appSettings.SwaggerUIEnabled)
      {
         services.AddSwaggerGen(c =>
         {
            c.SwaggerDoc(appSettings.ServiceName,
               new OpenApiInfo
               {
                  Title = appSettings.ServiceName,
                  Version = Assembly.GetEntryAssembly()?
                     .GetCustomAttribute<AssemblyInformationalVersionAttribute>()
                     .InformationalVersion ?? "NA"
               });
         });
      }

      // ...
   }

   public static void Configure(
      IApplicationBuilder app,
      IServiceProvider provider)
   {
      // ...

      if (appSettings.SwaggerUIEnabled)
      {
         app.UseStaticFiles()
            .UseSwagger()
            .UseSwaggerUI(c =>
            {
               c.SwaggerEndpoint(Url.Combine("/", "swagger.json"), appSettings.ServiceName);
            });
      }

      // ...
   }
}

the swaggerUIEnabled config setting will be true in all non-prod environments and false in prod.

Note: you'll need to host your Open API spec (swagger.json in code example above) statically under wwwroot.

Although @Ryan.Bartsch's answer is a valid approach, it misses a key point: Startup.Configure method is executed once at boot. When you change a configuration, IOptionsMonitor will reflect the changes, but as Configure method isn't executed again, the middleware pipeline will stay immutable. You have to restart the app to reconfigure the middleware.

To change the middleware at runtime, you can use IApplicationBuilder.UseWhen and optionally enable Swagger middleware. This lets you use any service or plain old IConfiguration (or IOptionsSnapshot) to decide when to enable Swagger.

From the docs:

MapWhen branches the request pipeline based on the result of the given predicate. Any predicate of type Func<HttpContext, bool> can be used to map requests to a new branch of the pipeline. ...

UseWhen also branches the request pipeline based on the result of the given predicate. Unlike with MapWhen, this branch is rejoined to the main pipeline if it doesn't short-circuit or contain a terminal middleware.

// remove default middleware
// app.UseSwagger(/*...*/);
// app.UseSwaggerUI(/*...*/);

app.UseWhen(context =>
{
    var swaggerToggle = context.RequestServices.GetRequiredService<SwaggerToggle>();
    var env = context.RequestServices.GetRequiredService<IWebHostEnvironment>();
    // enable swagger only in some cases, use a service, IConfiguration, ... anything, really.
    return env.IsDevelopment() || swaggerToggle.IsEnabled;
}, builder => builder.UseSwagger(/*...*/).UseSwaggerUI(/*...*/));

// ... other middleware

An example implementation of SwaggerToggle could be as follows:

public class SwaggerToggle
{
    public bool IsEnabled { get; set; }
}

// register the service as singleton
services.AddSingleton<SwaggerToggle>();

Then it can be controlled via an endpoint

public class SwaggerToggleController : ControllerBase
{
    private readonly SwaggerToggle _swaggerToggle;

    public SwaggerToggleController(SwaggerToggle swaggerToggle)
    {
        _swaggerToggle = swaggerToggle;
    }
    
    [Authorize]
    [HttpPost]
    public ActionResult Toggle(bool enabled)
    {
        _swaggerToggle.IsEnabled = enabled;
        return Ok(new { enabled });
    }
}
Related