How to omit methods from Swagger documentation on WebAPI using Swashbuckle
C#asp.net.NetSwaggerC# Problem Overview
I have a C# ASP.NET WebAPI application with API documentation being automatically generated using Swashbuckle. I want to be able to omit certain methods from the documentation but I can't seem to work out how to tell Swagger not to include them in the Swagger UI output.
I sense it is something to do with adding a model or schema filter but it isn't obvious what to do and the documentation only seems to provide examples of how to modify the output for a method, not remove it completely from the output.
C# Solutions
Solution 1 - C#
You can add the following attribute to Controllers and Actions to exclude them from the generated documentation: [ApiExplorerSettings(IgnoreApi = true)]
Solution 2 - C#
May help somebody but during development (debugging) we like to expose whole Controllers and/or Actions and then hide these during production (release build)
#if DEBUG
[ApiExplorerSettings(IgnoreApi = false)]
#else
[ApiExplorerSettings(IgnoreApi = true)]
#endif
Solution 3 - C#
Someone posted the solution on github so I'm going to paste it here. All credits goes to him. https://github.com/domaindrivendev/Swashbuckle/issues/153#issuecomment-213342771
Create first an Attribute class
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class HideInDocsAttribute : Attribute
{
}
Then create a Document Filter class
public class HideInDocsFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
foreach (var apiDescription in apiExplorer.ApiDescriptions)
{
if (!apiDescription.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<HideInDocsAttribute>().Any() && !apiDescription.ActionDescriptor.GetCustomAttributes<HideInDocsAttribute>().Any()) continue;
var route = "/" + apiDescription.Route.RouteTemplate.TrimEnd('/');
swaggerDoc.paths.Remove(route);
}
}
}
Then in Swagger Config class, add that document filter
public class SwaggerConfig
{
public static void Register(HttpConfiguration config)
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
config
.EnableSwagger(c =>
{
...
c.DocumentFilter<HideInDocsFilter>();
...
})
.EnableSwaggerUi(c =>
{
...
});
}
}
Last step is to add [HideInDocsAttribute] attribute on the Controller or Method you don't want Swashbuckle to generate documentation.
Solution 4 - C#
You can remove "operations" from the swagger document after it's generated with a document filter - just set the verb to null
(though, there may be other ways to do it as well)
The following sample allows only GET
verbs - and is taken from this issue.
class RemoveVerbsFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
foreach (PathItem path in swaggerDoc.paths.Values)
{
path.delete = null;
//path.get = null; // leaving GET in
path.head = null;
path.options = null;
path.patch = null;
path.post = null;
path.put = null;
}
}
}
and in your swagger config:
...EnableSwagger(conf =>
{
// ...
conf.DocumentFilter<RemoveVerbsFilter>();
});
Solution 5 - C#
Make a filter
public class SwaggerTagFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
foreach(var contextApiDescription in context.ApiDescriptions)
{
var actionDescriptor = (ControllerActionDescriptor)contextApiDescription.ActionDescriptor;
if(!actionDescriptor.ControllerTypeInfo.GetCustomAttributes<SwaggerTagAttribute>().Any() &&
!actionDescriptor.MethodInfo.GetCustomAttributes<SwaggerTagAttribute>().Any())
{
var key = "/" + contextApiDescription.RelativePath.TrimEnd('/');
swaggerDoc.Paths.Remove(key);
}
}
}
}
Make an attribute
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class SwaggerTagAttribute : Attribute
{
}
Apply in startup.cs
services.AddSwaggerGen(c => {
c.SwaggerDoc(1, new Info { Title = "API_NAME", Version = "API_VERSION" });
c.DocumentFilter<SwaggerTagFilter>(); // [SwaggerTag]
});
Add [SwaggerTag] attribute to methods and controllers you want to include in Swagger JSON
Solution 6 - C#
I would prefer to remove the dictionary entries for path items completely:
var pathsToRemove = swaggerDoc.Paths
.Where(pathItem => !pathItem.Key.Contains("api/"))
.ToList();
foreach (var item in pathsToRemove)
{
swaggerDoc.Paths.Remove(item.Key);
}
With this approach, you would not get "empty" items in the generated swagger.json definition.
Solution 7 - C#
Like @aleha I wanted to exclude by default so that I didn't accidentally expose an endpoint by accident (secure by default) but was using a newer version of the Swagger that uses OpenApiDocument.
Create a ShowInSwagger Attribute
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ShowInSwaggerAttribute : Attribute
{}
Then create a Document Filter
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System;
using System.Linq;
using TLS.Common.Attributes;
namespace TLS.Common.Filters
{
public class ShowInSwaggerFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var contextApiDescription in context.ApiDescriptions)
{
var actionDescriptor = (ControllerActionDescriptor)contextApiDescription.ActionDescriptor;
if (actionDescriptor.ControllerTypeInfo.GetCustomAttributes<ShowInSwaggerAttribute>().Any() ||
actionDescriptor.MethodInfo.GetCustomAttributes<ShowInSwaggerAttribute>().Any())
{
continue;
}
else
{
var key = "/" + contextApiDescription.RelativePath.TrimEnd('/');
var operation = (OperationType)Enum.Parse(typeof(OperationType), contextApiDescription.HttpMethod, true);
swaggerDoc.Paths[key].Operations.Remove(operation);
// drop the entire route of there are no operations left
if (!swaggerDoc.Paths[key].Operations.Any())
{
swaggerDoc.Paths.Remove(key);
}
}
}
}
}
}
then in your startup.cs or ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// other code
services.AddSwaggerGen(c =>
{
c.DocumentFilter<ShowInSwaggerFilter>();
// other config
});
}
Solution 8 - C#
Based on @spottedmahns answer. My task was vice versa. Show only those that are allowed.
Frameworks: .NetCore 2.1; Swagger: 3.0.0
Added attribute
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ShowInSwaggerAttribute : Attribute
{
}
And implement custom IDocumentFilter
public class ShowInSwaggerFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var contextApiDescription in context.ApiDescriptions)
{
var actionDescriptor = (ControllerActionDescriptor) contextApiDescription.ActionDescriptor;
if (actionDescriptor.ControllerTypeInfo.GetCustomAttributes<ShowInSwaggerAttribute>().Any() ||
actionDescriptor.MethodInfo.GetCustomAttributes<ShowInSwaggerAttribute>().Any())
{
continue;
}
else
{
var key = "/" + contextApiDescription.RelativePath.TrimEnd('/');
var pathItem = swaggerDoc.Paths[key];
if(pathItem == null)
continue;
switch (contextApiDescription.HttpMethod.ToUpper())
{
case "GET":
pathItem.Get = null;
break;
case "POST":
pathItem.Post = null;
break;
case "PUT":
pathItem.Put = null;
break;
case "DELETE":
pathItem.Delete = null;
break;
}
if (pathItem.Get == null // ignore other methods
&& pathItem.Post == null
&& pathItem.Put == null
&& pathItem.Delete == null)
swaggerDoc.Paths.Remove(key);
}
}
}
}
ConfigureServices code:
public void ConfigureServices(IServiceCollection services)
{
// other code
services.AddSwaggerGen(c =>
{
// other configurations
c.DocumentFilter<ShowInSwaggerFilter>();
});
}
Solution 9 - C#
Add one line to the SwaggerConfig
c.DocumentFilter<HideInDocsFilter>();
...
public class HideInDocsFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
var pathsToRemove = swaggerDoc.Paths
.Where(pathItem => !pathItem.Key.Contains("api/"))
.ToList();
foreach (var item in pathsToRemove)
{
swaggerDoc.Paths.Remove(item.Key);
}
}
}
Solution 10 - C#
You can create a custom filter at both Controller and Method level. So any Controller/Method with your attribute will be available in the Swagger doc. This filter also removed the duplicate HTTP verbs from your document (in this example I make it for GET/PUT/POST/PATCH only), however, you can always customize per your requirement
The attribute
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class PublicApi:Attribute
{
}
Document filter
public class PublicApiFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
var publicPaths = new List<string> {"/api"};
var publicApiDescriptions = new List<ApiDescription>();
var publicMethods = FilterByPublicControllers(swaggerDoc, apiExplorer, publicPaths, publicApiDescriptions);
FilterByPublicActions(swaggerDoc, publicApiDescriptions, publicMethods);
}
private static Dictionary<string, List<string>> FilterByPublicControllers(SwaggerDocument swaggerDoc, IApiExplorer apiExplorer, List<string> publicPaths, List<ApiDescription> publicApiDescriptions)
{
var publicMethods = new Dictionary<string, List<string>>();
foreach (var apiDescription in apiExplorer.ApiDescriptions)
{
var isPublicApiController = apiDescription.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<PublicApi>().Any();
var isPublicApiMethod = apiDescription.ActionDescriptor.GetCustomAttributes<PublicApi>().Any();
if (!isPublicApiController && !isPublicApiMethod)
{
continue;
}
var relativePath = ToRelativePath(apiDescription);
publicPaths.Add(relativePath);
publicApiDescriptions.Add(apiDescription);
var action = apiDescription.ActionDescriptor.ActionName;
List<string> available = null;
if (!publicMethods.TryGetValue(relativePath, out available))
publicMethods[relativePath] = new List<string>();
publicMethods[relativePath].Add(action);
}
swaggerDoc.paths = swaggerDoc.paths.Where(pair => publicPaths.Contains(pair.Key))
.ToDictionary(pair => pair.Key,
pair => pair.Value);
return publicMethods;
}
private static void FilterByPublicActions(SwaggerDocument swaggerDoc, List<ApiDescription> publicApis, Dictionary<string, List<string>> publicMethods)
{
foreach (var api in publicApis)
{
var relativePath = ToRelativePath(api);
var availableActions = publicMethods[relativePath];
if (availableActions == null)
{
continue;
}
foreach (var path in swaggerDoc.paths.Where(pair => pair.Key.IndexOf(relativePath) > -1).ToList())
{
if (!availableActions.Contains("Get"))
path.Value.get = null;
if (!availableActions.Contains("Post"))
path.Value.post = null;
if (!availableActions.Contains("Put"))
path.Value.put = null;
if (!availableActions.Contains("Patch"))
path.Value.patch = null;
}
}
}
private static string ToRelativePath(ApiDescription apiDescription)
{
return "/" + apiDescription.RelativePath.Substring(0,apiDescription.RelativePath.LastIndexOf('/'));
}
}
And finally, register your SwaggerConfig
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "Reports");
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
c.DocumentFilter<PublicApiFilter>();
})
.EnableSwaggerUi(c =>
{
});
}
}
Examples
Controller
[PublicApi]
public class ProfileController : ApiController
Method
public class UserController : ApiController
{
[PublicApi]
public ResUsers Get(string sessionKey, int userId, int groupId) {
return Get(sessionKey, userId, groupId, 0);
}
Solution 11 - C#
If you are using the minimal API you can use:
app.MapGet("/hello", () => "Hello World!").ExcludeFromDescription();