Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude controllers methods from docs without using the Obsolete attribute #90 #153

Closed
ghost opened this issue Dec 29, 2014 · 25 comments
Closed

Comments

@ghost
Copy link

ghost commented Dec 29, 2014

If i want to Individual Method in Controller instead of whole Controller Ignore what can I do?

@domaindrivendev
Copy link
Owner

Swashbuckle is built on top of WebApi's built-in metadata layer - ApiExplorer. If you decorate a controller or action with the following attribute:

[ApiExplorerSettings(IgnoreApi=true)]
public class MyController : ApiController

then this will ultimately cause the entire controller or individual action to be omitted from the Swagger output

@abinashTGG
Copy link

I have a scenario where I have a BaseController which each API inherits. I want to hide this BaseController. The moment I put the below line all controllers are hidden from the documentation.
[ApiExplorerSettings(IgnoreApi = true)] //Making sure this does not appear in the documentation

How do we handle this?

@brutaldev
Copy link

+1 for hiding just the BaseController.

@Misiu
Copy link

Misiu commented Apr 22, 2016

I've managed to do this by creating custom attribute:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class HideInDocsAttribute:Attribute
{
}

and custom IDocumentFilter:

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);
        }
    }
}

this way every method that has [HideInDocs] attribute will be excluded.

EDIT: You can apply this attribute to Controller and have all it methods excluded.

@ppozeti
Copy link

ppozeti commented Feb 14, 2017

@Misiu 👍
Your solution works well! I just want to add that you need specify in SwaggerConfig class the following:
c.DocumentFilter<HideInDocsFilter>();

@ahmettahasakar
Copy link

Hello,

This doesnt work for me.

apiDescription.Route.RouteTemplate

is always

api/{controller}/{id}

Therefore, it can not remove it. How do I fix it? I dont want to manually define a new routemap for each controller.

Thank you.

@SlyNet
Copy link

SlyNet commented May 17, 2017

If it was possible to do the opposite from defaults - include only controllers that are decorated with specific attribute. When app has multiple controllers that are not needed in docs and only some that are needed.

@heldersepu
Copy link
Contributor

@SlyNet
Take a look at the comment from Misiu:
#153 (comment)
With a custom IDocumentFilter you can hide or show anything you want.

@heldersepu
Copy link
Contributor

@SlyNet
Here is an example where we hide ALL and only leave some

        private class ApplyDocumentVendorExtensions : IDocumentFilter
        {
            public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
            {
                var paths = new Dictionary<string, PathItem>(swaggerDoc.paths);
                swaggerDoc.paths.Clear();
                foreach (var path in paths)
                {
                    if (path.Key.Contains("foo"))
                        swaggerDoc.paths.Add(path);
                }
            }
        }

@AndrewJamesMills
Copy link

For someone that comes across this and is using ODataControllers and not API Controller, I had to update the assigning of route as below. This might also work for the problem @ahmettahasakar ran into.

var route = "/" + apiDescription.RelativePath.TrimEnd('/');

@blwillia
Copy link

blwillia commented Mar 21, 2018

I did something a little different for OData. The solution above was removing more than one method of the controller, often a GET and POST at the same time. I adjusted this a bit so it'll work on the methods it's attached to. If there is a more condensed version that someone can think of, that'll be great.

Would update this more to check to see if all methods in the controller are being ignored, then remove the path.

public class HideInSwaggerDocumentFilter : IDocumentFilter
    {
        public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
        {
            foreach (var apiDescription in apiExplorer.ApiDescriptions)
            {
                if (!apiDescription.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<HideInSwaggerAttribute>().Any() && !apiDescription.ActionDescriptor.GetCustomAttributes<HideInSwaggerAttribute>().Any()) continue;
                var route = "/" + apiDescription.RelativePath.TrimEnd('/');

                if (apiDescription.ActionDescriptor.ActionName.ToLower() == "post")
                    swaggerDoc.paths[route].post = null;
                else if (apiDescription.ActionDescriptor.ActionName.ToLower() == "get")
                    swaggerDoc.paths[route].get = null;
                else if (apiDescription.ActionDescriptor.ActionName.ToLower() == "patch")
                    swaggerDoc.paths[route].patch = null;
                else if (apiDescription.ActionDescriptor.ActionName.ToLower() == "delete")
                    swaggerDoc.paths[route].delete = null;
                else
                    swaggerDoc.paths.Remove(route);
            }
        }
    }

@Cloudmersive
Copy link

Why isn't there just a simple attribute to SKIP a method? I feel like Swashbuckle is a lot harder to use than it needs to be; everything requires building your own document filter when the most common primitives could have been put in the box

@heldersepu
Copy link
Contributor

@Cloudmersive Don't forget Swashbuckle is OpenSource!
Wanna make it less harder to use? create a fork send a pull request...
...you know, walk the walk or better yet code the code

@domaindrivendev
Copy link
Owner

Did you read the first response to this issue? What’s your problem with the ApiExplorerSettings attribute?

@whippet-rider
Copy link

Might have missed the point that it can be added to individual actions @domaindrivendev ?

@rvdb2
Copy link

rvdb2 commented Apr 26, 2018

To hide a custom BaseController, you can make it abstract.

public abstract class BaseController : ApiController

@waqasdilawar
Copy link

@rvdb2 abstract class doesn't hide the an custom controller. I'm trying in Azure Service Fabric ASP.NET Core Stateless service.

@rvdb2
Copy link

rvdb2 commented May 7, 2018

@waqasdilawar I retried it using a new Web API project in VS2015, with 2 empty controllers: public abstract class CustomBaseController : ApiController, and public CustomController : CustomBaseController. CustomBaseController has one concrete method: public string Get(int id), while CustomController is empty.

If I run the project in VS2015 (using IIS), CustomBaseController.Get is unaccessible and hidden in documentation, while the inherited CustomController.Get is accessible and visible.

If I make CustomBaseController non-abstract, its Get method becomes accessible and visible.

Hopefully this makes it reproducible for you.

@dotsad
Copy link

dotsad commented Aug 7, 2018

@Misiu 👍
Your solution works well! I just want to add that i had to change your code from:

var route = "/" + apiDescription.Route.RouteTemplate.TrimEnd('/');
to
var route = "/" + apiDescription.RelativePath.TrimEnd('/');

otherwise RouteTemplate is always

api/{controller}/{id}

@mihair2002
Copy link

Here is a sample ShowInSwagger implementation:

using System.Web.Http;
using Swashbuckle.Application;
using System;
using System.Reflection;
using System.IO;
using System.Linq;
using Swashbuckle.Swagger;
using System.Web.Http.Description;
using System.Collections.Generic;

public class SwaggerConfig
{
	public static void Register(HttpConfiguration config)
	{
		config.EnableSwagger(c =>
			{
				c.DocumentFilter<ShowInSwaggerAttributeFilter>();
				c.SingleApiVersion("v1", "MyName");
				var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
				var commentsFileName = Assembly.GetExecutingAssembly().GetName().Name + ".XML";
				var commentsFile = Path.Combine(baseDirectory, "bin", commentsFileName);
				c.IncludeXmlComments(commentsFile);
			})
		.EnableSwaggerUi(c =>
		{
			c.DisableValidator();
		});
	}
}

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ShowInSwaggerAttribute : Attribute
{
}

////https://github.com/domaindrivendev/Swashbuckle/issues/153
public class ShowInSwaggerAttributeFilter : IDocumentFilter
{
	public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
	{
		var filteredApis = apiExplorer.ApiDescriptions.Where(a => a.GetControllerAndActionAttributes<ShowInSwaggerAttribute>().Any());

		var paths = new Dictionary<string, PathItem>(swaggerDoc.paths);

		swaggerDoc.paths.Clear();

		foreach (var apiDescription in filteredApis)
		{
			var route = "/" + apiDescription.RelativePathSansQueryString();
			if (paths.Any(p => p.Key == route))
			{
				var path = paths.FirstOrDefault(p => p.Key == route);
				swaggerDoc.paths.Add(path);
			}
		}
	}
}

To use just add the [ShowInSwagger] attribute to the controller class or method.

@vkras
Copy link

vkras commented May 28, 2020

Improvement to take verb (GET/POST/etc) into consideration:

`public class HideInDocsFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var contextApiDescription in context.ApiDescriptions)
{
var actionDescriptor = (ControllerActionDescriptor)contextApiDescription.ActionDescriptor;

            if (actionDescriptor.ControllerTypeInfo.GetCustomAttributes(typeof(HideInSwaggerAttribute), true).Any() ||
                actionDescriptor.MethodInfo.GetCustomAttributes(typeof(HideInSwaggerAttribute), true).Any())
            {
                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);
                }
            }
        }
    }
}`

@shobhit-github
Copy link

Use @ApiExcludeEndpoint(true) for NestJS

@gsingh203
Copy link

I have a scenario where I have a BaseController which each API inherits. I want to hide this BaseController. The moment I put the below line all controllers are hidden from the documentation.
[ApiExplorerSettings(IgnoreApi = true)] //Making sure this does not appear in the documentation

How do we handle this?

I had the same issue. Set your BaseController access modifier to "Protected" instead of "Public". This will hide the Controller showing up in Swagger. It worked for me.

@sumedhkalaskar
Copy link

I had similar issue, where i want to show only 1 controller in documentation out of 80+ Controller with 400+ action methods.
my approach is to keep [ApiExplorerSettings(IgnoreApi = true)] on my "ApiBaseController : ApiController" which inherit by all Controllers and then keep [ApiExplorerSettings(IgnoreApi = false)] to that single controller which i want to show in documentation.
Hope this work for you.

@dn32
Copy link

dn32 commented Dec 10, 2021

You can add the following attribute to Controllers and Actions to exclude them from the generated documentation:
[ApiExplorerSettings(IgnoreApi = true)]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests