In a Web API I'm building, controllers are routed using this convention:
[Route("api/public/v{version:apiVersion}/[controller]")]
[ApiController]
public class TestController:ControllerBase
or
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class InternalController:ControllerBase
This generates swagger ui endpoint URLS like
/api/public/v1.0/Test
So far, so good. I'd like though to simplify the display of those in Swagger UI, so I thought maybe if I use swagger Servers
property, I could simplify the endpoints to have a format like this:
/v1.0/Test
app.UseSwagger(c=>
{
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
swaggerDoc.Servers = new List<OpenApiServer>
{
new OpenApiServer {
Url = "/api/public"
},
new OpenApiServer {
Url = "/api"
}
};
});
});
This adds a server dropdown in the swagger UI, but endpoint is unaffected.
Requests are routed to /api/public/api/public/v1.0/Test
I also modified the controller to use this as route:
[Route("v{version:apiVersion}/[controller]")]
This makes the Swagger UI display as I'd like:
But I get 404 errors when I try it out, even though the "path" looks ok to me: /api/public/v1.0/Test
What am I missing?
In order to make it work, you also need to take care of paths in the documentation - you have to align them accordingly to how you set up your server path.
I have implemented IDocumentFilter
for full implementation:
public class DocumentFilter : IDocumentFilter
{
const string UnwantedPrefix = "/api/public";
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
GenerateNewPaths(swaggerDoc);
GenerateServers(swaggerDoc);
}
private static void GenerateServers(OpenApiDocument swaggerDoc)
{
swaggerDoc.Servers = new List<OpenApiServer>
{
new OpenApiServer {
Url = UnwantedPrefix
},
};
}
private static void GenerateNewPaths(OpenApiDocument swaggerDoc)
{
var newPaths = new OpenApiPaths();
foreach (var path in swaggerDoc.Paths)
{
newPaths.Add(path.Key.Replace(UnwantedPrefix, string.Empty), path.Value);
}
swaggerDoc.Paths = newPaths;
}
}
Then use it in registration code:
services.AddSwaggerGen(c =>
{
c.DocumentFilter<DocumentFilter>();
});