Search code examples
c#swaggerasp.net-core-webapiswagger-uiswashbuckle

How to replace Swagger "Server" path in Swagger UI Endpoint route?


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

Generated swagger UI

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

What i've tried:

Attempt #1:
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

Attempt #2:

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:

Generated swagger UI after attempt 2

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?


Solution

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