23

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!

Subash Kharel
  • 478
  • 1
  • 3
  • 12
  • 1
    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. – benPearce Feb 13 '17 at 03:30

6 Answers6

30

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
    {
      ...
    }
}
12

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));
        }
    }
}
Rory
  • 3,270
  • 1
  • 20
  • 23
  • Pleasure @SUBASH. I'm glad it's working for you now. If this or any answer has solved your question please consider [accepting it](https://meta.stackexchange.com/q/5234/179419) 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. – Rory Mar 29 '17 at 12:25
  • swaggerDoc.paths.Remove("/" + apiDescription.Route.RouteTemplate.TrimEnd('/')); did not remove anything. – madufit1 Sep 20 '18 at 09:48
5

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");
    }
}
Andrew Samole
  • 695
  • 7
  • 7
  • Where should I put this code? same dir with startup.cs? – linjunhalida Feb 27 '20 at 09:33
  • 1
    Sure, it doesn't really matter. Just make sure you add this to your Startup.cs services.AddSwaggerGen(options => options.DocumentFilter(); – Andrew Samole Feb 27 '20 at 18:27
  • Take a look at this tutorial if you get lost: https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-3.1&tabs=visual-studio – Andrew Samole Feb 27 '20 at 18:31
  • 1
    The version I'm using, 5.6.1, uses `OpenApiDocument` instead of `SwaggerDocument`. There are probably other versions that do the same. – computercarguy Sep 28 '20 at 17:56
2

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);
            }
        }
    }
}
Arkistarvh Kltzuonstev
  • 6,824
  • 7
  • 26
  • 56
madufit1
  • 206
  • 3
  • 10
1

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

dvdmn
  • 6,456
  • 7
  • 44
  • 52
  • 1
    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 Aug 21 '20 at 10:17
  • You need to edit your answer and not add a comment. – Anouar Aug 24 '20 at 08:14
0

You can use DocInclusionPredicate to Customize the Action Selection Process:

When selecting actions for a given Swagger document, the generator invokes a DocInclusionPredicate against every ApiDescription that's surfaced by the framework. The default implementation inspects ApiDescription.GroupName and returns true if the value is either null OR equal to the requested document name. However, you can also provide a custom inclusion predicate.

services.AddSwaggerGen(c =>
{
    c.DocInclusionPredicate((string title, ApiDescription apiDesc) =>
    {
        // filter the ApiDescription 
    });
});
Métoule
  • 13,062
  • 2
  • 56
  • 84