Show only selected controllers in swagger-swashbuckle UI

17,534

Solution 1

you can use ApiExplorerSettings on either controller to ignore a controller completely or on a method.

[ApiExplorerSettings(IgnoreApi = true)]
public class MyController
{
    [ApiExplorerSettings(IgnoreApi = true)]
    public string MyMethod
    {
      ...
    }
}

Solution 2

Using swashbuckle's document filter you can remove some elements of the generated specification after the fact, and they would then not be included on the integrated swagger-ui. Create a class such as the below:

using System;
using System.Web.Http.Description;
using Swashbuckle.Swagger;

internal class SwaggerFilterOutControllers : IDocumentFilter
{
    void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        foreach (ApiDescription apiDescription in apiExplorer.ApiDescriptions)
        {
            Console.WriteLine(apiDescription.Route.RouteTemplate);

            if ((apiDescription.RelativePathSansQueryString().StartsWith("api/System/"))
                || (apiDescription.RelativePath.StartsWith("api/Internal/"))
                || (apiDescription.Route.RouteTemplate.StartsWith("api/OtherStuff/"))
                )
            {
                swaggerDoc.paths.Remove("/" + apiDescription.Route.RouteTemplate.TrimEnd('/'));
            }
        }
    }
}

and then edit your SwaggerConfig.cs file to include the filter:

        GlobalConfiguration.Configuration
            .EnableSwagger(c =>
                    c.DocumentFilter<SwaggerFilterOutControllers>();

Note that while the controllers have been removed from the specification, other items such as the result models will still be included in the specification and might still be slowing down the page load.

It could also be slow simply due to enumerating all of the controllers/models etc in the first place, in which case this might not help.

Edit: I noticed it would regenerate the whole definition every time the UI page was viewed (which could be crippling in your scenario). Fortunately it's super easy to cache this (which should be fine as it shouldn't change at runtime for the majority of people).

Add this to your config:

c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));

and use this class shamelessly copied from https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Dummy.Core/App_Start/CachingSwaggerProvider.cs

using Swashbuckle.Swagger;
using System.Collections.Concurrent;

namespace <your namespace>
{
    public class CachingSwaggerProvider : ISwaggerProvider
    {
        private static ConcurrentDictionary<string, SwaggerDocument> _cache =
            new ConcurrentDictionary<string, SwaggerDocument>();

        private readonly ISwaggerProvider _swaggerProvider;

        public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
        {
            _swaggerProvider = swaggerProvider;
        }

        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
        {
            string cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
            return _cache.GetOrAdd(cacheKey, (key) => _swaggerProvider.GetSwagger(rootUrl, apiVersion));
        }
    }
}

Solution 3

In response to the previous answer, this is the updated code for ASP.NET Core. I also added the feature to remove models.

using System;
using System.Linq;
using System.Web.Http;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.Swagger;

using Microsoft.AspNetCore.Mvc.ApiExplorer;

internal class SwaggerFilterOutControllers : IDocumentFilter
{

    void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
    {
        foreach (var item in swaggerDoc.Paths.ToList())
        {
            if (!(item.Key.ToLower().Contains("/api/endpoint1") ||
                  item.Key.ToLower().Contains("/api/endpoint2")))
            {
                swaggerDoc.Paths.Remove(item.Key);
            }
        }

        swaggerDoc.Definitions.Remove("Model1");
        swaggerDoc.Definitions.Remove("Model2");
    }
}

Solution 4

swaggerDoc.paths.Remove("/" + apiDescription.Route.RouteTemplate.TrimEnd('/'));did not remove anything for me. So,

internal class SwaggerFilterOutControllers : IDocumentFilter
{
    void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        foreach (var item in swaggerDoc.Paths.ToList())
        {
            if (!(item.Key.ToLower().Contains("/api/v1/xxxx") ||
                  item.Key.ToLower().Contains("/api/v1/yyyy")))
            {
                swaggerDoc.Paths.Remove(item.Key);
            }
        }
    }
}

Solution 5

You can try this. You APIExplorerSetting to specify APIs to be included in a particular group.

Start by defining multiple Swagger docs in Startup.cs:

services.AddSwaggerGen(c => { 
    c.SwaggerDoc("v1", new OpenApiInfo { 
        Title = "My API - V1",
        Version = "v1" 
    });
    c.SwaggerDoc("v2", new OpenApiInfo {
        Title = "My API - V2",
        Version = "v2"
    });
});

Then decorate the individual controller with the above groups:

[ApiExplorerSettings(GroupName = "v2")]

Reference: https://github.com/domaindrivendev/Swashbuckle.AspNetCore#generate-multiple-swagger-documents

Share:
17,534

Related videos on Youtube

Subash Kharel
Author by

Subash Kharel

Updated on September 15, 2022

Comments

  • Subash Kharel
    Subash Kharel about 1 year

    I am currently using swagger in my project and i have more than 100 controllers there. I guess due to the large number of controller, swagger UI documentation page takes more than 5 min to load its controller. Is it possible to select specific controllers at the UI page and load options for them only? Or else there are other methods to load UI page faster? Help me!

    • benPearce
      benPearce over 6 years
      Perhaps the number of controllers is the problem? Consolidate or split to separate domain applications. One hundred controllers in a single app does represent a bit of a smell.
  • Subash Kharel
    Subash Kharel over 6 years
    I need to select the controller dynamically. I want to have a option in swagger UI and select controllers in the option and load them dynamically. Is it possible?
  • Rory
    Rory over 6 years
    Pleasure @SUBASH. I'm glad it's working for you now. If this or any answer has solved your question please consider accepting it by clicking the check-mark. This indicates to the wider community that you've found a solution and gives some reputation to both the answerer and yourself. There is no obligation to do this.
  • madufit1
    madufit1 about 5 years
    swaggerDoc.paths.Remove("/" + apiDescription.Route.RouteTemplate.TrimEnd('/')); did not remove anything.
  • mrmashal
    mrmashal almost 4 years
    using Microsoft.AspNetCore.Mvc;
  • linjunhalida
    linjunhalida over 3 years
    Where should I put this code? same dir with startup.cs?
  • Andrew Samole
    Andrew Samole over 3 years
    Sure, it doesn't really matter. Just make sure you add this to your Startup.cs services.AddSwaggerGen(options => options.DocumentFilter<SwaggerFilterOutControllers>();
  • Andrew Samole
    Andrew Samole over 3 years
    Take a look at this tutorial if you get lost: docs.microsoft.com/en-us/aspnet/core/tutorials/…
  • Anouar
    Anouar about 3 years
    Please include a sample code from the suggested solution. Then add a link to the documentation. Only posting a link to some doc is not enough as the link can become stale in the future.
  • Anouar
    Anouar about 3 years
    You need to edit your answer and not add a comment.
  • computercarguy
    computercarguy about 3 years
    The version I'm using, 5.6.1, uses OpenApiDocument instead of SwaggerDocument. There are probably other versions that do the same.
  • CodeHacker
    CodeHacker over 2 years
    What if I want this to be enabled/disabled from the AppsSettings (Options<MyApplicationSettings>)?