When developing APIs, it is common to come across scenarios where we need to hide an endpoint from the public. In this article, we will explore various methods to achieve this goal, focusing on the Swashbuckle library in an ASP.NET application.
So let’s dive into the details and learn how to effectively hide API methods in Swagger.
Hiding Endpoint in Swagger
In general, Swagger is an open-source framework that helps us design, build, document, and consume RESTful Web services. It provides us with clear and detailed documentation of API endpoints and their expected inputs and outputs. All this leads to an easier way to share and maintain our web services.
There can be a variety of reasons why we wouldn’t want to expose an endpoint in our documentation:
- Sensitive operations
- Deprecated endpoints
- Internal endpoints
- Not fully implemented endpoints
When we hide an endpoint in Swagger, we exclude it from the Swagger-generated API documentation, and it won’t be visible in the API documentation’s user interface. As a result, we won’t be able to explore the endpoint’s details and test it using Swagger.
Now that we know a little bit more about Swagger and potential reasons why we would want to hide an endpoint, let’s see it in action.
Project Setup
To start it off, we will create a new ASP.NET Core Web API project. We can do it using the ASP.NET Core Web API template in Visual Studio, or using .NET CLI:
dotnet new webapi
To show different ways of hiding an endpoint, let’s extend the default WeatherForecast
controller with a few methods of our own:
[HttpGet("GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return GetWeatherForecastData(); } [HttpGet("GetMethodOne")] [NonAction] public IEnumerable<WeatherForecast> GetMethodOne() { return GetWeatherForecastData(); } [HttpGet("GetMethodTwo")] [ApiExplorerSettings(IgnoreApi = true)] public IEnumerable<WeatherForecast> GetMethodTwo() { return GetWeatherForecastData(); } [HttpGet("GetMethodThree")] public IEnumerable<WeatherForecast> GetMethodThree() { return GetWeatherForecastData(); } [HttpGet("GetMethodFour")] public IEnumerable<WeatherForecast> GetMethodFour() { return GetWeatherForecastData(); } private IEnumerable<WeatherForecast> GetWeatherForecastData() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); }
We expand the existing GetWeatherForecast
controller with four more methods that we can use to experiment with hiding endpoints. It’s important to note that we have kept the names of the methods and the endpoint URIs straightforward for simplicity and in real-life scenarios we should follow the REST URI formatting.
Now, if we run our application, we will end up on http://localhost:<port>/swagger/index.html
where we can see how all the routes are documented in Swagger:
Since now we have everything ready, let’s try to hide each one of these methods differently.
Using DocInclusionPredicate
The first way we can go about hiding an endpoint in Swagger is to use DocInclusionPredicate()
.
DocInclusionPredicate()
is a delegate that is invoked against every ApiDescription
that’s surfaced by our application. It takes two parameters, docName
and apiDesc
. docName
refers to the name of the Swagger document, which can be useful to distinguish between multiple Swagger documents.
Meanwhile, apiDesc
parameter represents the API description, which contains information about the processed endpoint.
We can set up DocInclusionPredicate()
when configuring Swagger, and customize it to filter the endpoints and operations included in the generated Swagger documentation.
So let’s say we want to hide our first endpoint named GetWeatherForecast
:
builder.Services.AddSwaggerGen(c => { c.DocInclusionPredicate((docName, apiDesc) => { var routeTemplate = apiDesc.RelativePath; if (routeTemplate == "WeatherForecast/GetWeatherForecast") return false; return true; }); });
First, we extract the relative path of the API endpoint that is being evaluated in the routeTemplate
variable. After that, we check if the endpoint routeTemplate
matches "WeatherForecast/GetWeatherForecast"
.
If it matches, we return false
, which means that the API endpoint will be excluded from the Swagger documentation. In any other case, we return true
, meaning that the endpoint will be included in the Swagger documentation.
Using NonAction Attribute
One more thing we can do when we want to hide an endpoint from Swagger is to use NonAction
attribute on the endpoint method we want to hide:
[HttpGet("GetMethodOne")] [NonAction] public IEnumerable<WeatherForecast> GetMethodOne() { return GetWeatherForecastData(); }
By using NonAction
attribute we indicate that the controller method is not an action method. Since Swagger relies on the API actions to generate the documentation, it will ignore any endpoint we mark with this attribute during the scanning process, and therefore the endpoint will not appear in the Swagger documentation. It also hides the public availability of the action.
It is important to note that we can apply this attribute only to controller endpoints and not the whole controller itself.
Using ApiExplorerSettings Attribute With the IgnoreApi Parameter
Another way to hide an endpoint from Swagger is to use the ApiExplorerSettings
attribute with the IgnoreApi
parameter set to true
:
[HttpGet("GetMethodTwo")] [ApiExplorerSettings(IgnoreApi = true)] public IEnumerable<WeatherForecast> GetMethodTwo() { return GetWeatherForecastData(); }
When using the [ApiExplorerSettings(IgnoreApi = true)]
attribute, we instruct the API Explorer responsible for generating the Swagger documentation, to exclude a specific endpoint from the generated documentation.
Contrary to the NonAction
attribute, we can use this attribute on the controller level, and hide all endpoints inside it.
Using IActionModelConvention
What can also come in handy when trying to hide an endpoint in Swagger is IActionModelConvention
.
In general, IActionModelConvention
is an interface that we can use to apply conventions to individual action methods. To use it, we need to implement the interface and the Apply()
method that takes an ActionModel
object as a parameter and applies desired conventions to it:
public class HideControllerConvention : IActionModelConvention { public void Apply(ActionModel action) { if (action.ActionName == "GetMethodThree") { action.ApiExplorer.IsVisible = false; } } }
In our case, we want to hide the GetMethodThree
endpoint. First, we check the action ActionName
property to find the wanted action name, and after we have it, we set its visibility to false.
Now, when we set up the desired convention, we also need to register it in our Program.cs
when adding controllers to make everything work as expected:
builder.Services.AddControllers(s => s.Conventions.Add(new HideControllerConvention() ));
Using IDocumentFilter
The last route we will take in hiding the Swagger endpoint is to use IDocumentFilter
.
The IDocumentFilter
interface is part of Swashbuckle.AspNetCore.SwaggerGen
namespace and it allows us to modify the generated Swagger document before it’s presented. In our case, we create SwaggerDocumentFilter
class which implements the IDocumentFilter
interface:
public class SwaggerDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { swaggerDoc.Paths.Remove("/WeatherForecast/GetMethodFour"); } }
Inside the Apply()
method, we have access to the OpenApiDocument
and the DocumentFilterContext
. The OpenApiDocument
represents the generated Swagger document, and DocumentFilterContext
provides us with additional context during the document generation process.
By calling swaggerDoc.Paths.Remove("/WeatherForecast/GetMethodFour")
, we remove the entry for this specific endpoint from the Paths
property of the Swagger document, In this way, we are making sure that the method will be hidden from the generated Swagger documentation.
Lastly, we need to add the document filter when configuring Swagger in our Program.cs
:
builder.Services.AddSwaggerGen(c => { c.DocumentFilter<SwaggerDocumentFilter>(); c.DocInclusionPredicate((docName, apiDesc) => { var routeTemplate = apiDesc.RelativePath; if (routeTemplate == "WeatherForecast/GetWeatherForecast") return false; return true; }); });
Now, if we rerun our application, we can see that the generated spec file does not present any of the endpoints:
When to Use Each Method
In conclusion, the choice of which method to use depends on our requirements and the level of control we need.
For simple cases where we need to hide individual endpoints, using the IgnoreApi
parameter of the ApiExplorerSettings
attribute or the NonAction
attribute can be sufficient. Their usage is the most straightforward one.
It’s worth noting that, the NonAction
attribute hides the endpoint from the documentation and it completely disables it and we can’t reach it with HTTP calls, unlike the other techniques where the endpoint is hidden from the documentation but will stay reachable by HTTP calls targeting it.
For situations requiring more advanced filtering or transformations, the IActionModelConvention
can be an excellent choice. We can hide an endpoint in Swagger based on any custom filter on all the properties available in the ActionModel
class.
Using DocInclusionPredicate()
and IDocumentFilter
work similarly. They both give us access to the Swagger document name and the ApiDescription
object. One benefit of the DocInclusionPredicate()
is that it is configured directly when setting up a Swagger, and it doesn’t require any additional interface implementations.
Conclusion
In this article, we looked at different ways to hide API endpoints in Swagger documentation. We explored using DocInclusionPredicate()
for direct configuration, as well as simpler options like the IgnoreApi
parameter and the NonAction
attribute. Lastly, we covered more advanced customization using IDocumentFilter
and IActionModelConvention
interfaces, that provide us with a range of choices to hide endpoints.