skip to Main Content

To allow us some simple, fast and accessible debugging and smoke testing of our services, we use the Swagger Web UI with the "Try it out" function.
This works well for local development and gives fast feedback.

These services are deployed in Kubernetes and each service we create and deploy is accessible via its own subpath on the main domain of the application, e.g.:

  • https://app.example.org/book-import/swagger/...
  • https://app.example.org/order-export/swagger/...
  • ...

The Swagger UI is accessible via https://app.example.org/book-import/swagger/index.html but the URLs generated in the UI still assume "no sub path" and the URLs for the "Try it out" requests look like this: https://app.example.org/api/... instead of https://app.example.org/book-import/api/...

I tried to use the PreSerializeFilters but it behaves not as expected.
This is what I have currently:

app.MapSwaggerDefaults(builder.Configuration, swaggerOptions: options =>
{
    options.PreSerializeFilters.Add((swagger, httpReq) =>
    {
        app.Services.GetRequiredService<ILogger<Program>>().LogInformation("Swagger path: {Path}", httpReq.Path.Value);
        if (httpReq.Path.Value != null && !httpReq.Path.Value.StartsWith("/swagger"))
        {
            swagger.Servers = new List<OpenApiServer> { new OpenApiServer { Url = httpReq.Path.Value.Replace("/swagger", "!").Split('!')[0] } };
        }
    });
});

I am trying to replace the /swagger part with the whole request URL if it’s not starting with /swagger.
Running this version of the app locally in Visual Studio "works", because I can set a break point in the if (httpReq.Path.Value != ... and it breaks there. But if I run this same code via the Visual Studio Kubernetes Bridge on a fully deployed cluster or build and push a new image containing this change and deploying it to the fully deployed cluster, it does not work.
The URLs are not changed in the Swagger UI and I dont even see the log output Swagger path:

How can i set the URLs for the "Try it out" feature of the Swagger UI, so that it works when the app is running in a sub path?
Or do these PreSerializeFilters work differently on K8s?

What I also tried, without success:

builder.Services.AddSwaggerGen(o => 
    o.SwaggerGeneratorOptions.Servers = new List<OpenApiServer> { new OpenApiServer { Url = "/book-import" }
});

.NET 8
Swashbuckle.AspNetCore 6.8.1

2

Answers


  1. Chosen as BEST ANSWER

    After trying numerous options and ways to rewrite the URL in the "Try it out" tab, I have now written a Pre-Serializer-Filter which reads the X-Forwarded Headers and rebuilds the Swagger Server URL if needed:

    app.MapSwaggerDefaults(builder.Configuration, swaggerOptions: options =>
    {
        options.PreSerializeFilters.Add((swagger, httpReq) =>
        {
            if (!string.IsNullOrEmpty(httpReq.Host.Value) && !httpReq.Host.Value.StartsWith("localhost"))
            {
                var forwardedProtos = httpReq.Headers["X-Forwarded-Proto"];
                var forwardedHosts = httpReq.Headers["X-Forwarded-Host"];
                var forwardedPrefix = httpReq.Headers["x-forwarded-prefix"];
    
                var proto = forwardedProtos.FirstOrDefault()?.Split(',')[0];
                var host = forwardedHosts.FirstOrDefault()?.Split(',')[0];
                var prefix = forwardedPrefix.FirstOrDefault()?.Split(',')[0];
    
                var rebuiltUrl = $"{proto}://{host}{prefix}";
    
                swagger.Servers = new List<OpenApiServer> { new OpenApiServer { Url = rebuiltUrl } };
            }
        });
    });
    

  2. I assume that your .net app has a reverse proxy in front of it that allows you to you to proxy each sub path to your different applications ?

    I can see two ways you can sort this problem :

    1. If the reverse proxy use X-Forwarded headers. You could use the forwarded header middleware with ForwardedHeaders. XForwardedPrefix
    2. Worst come to worst : adjust the base path of the application with app.UsePathBase(app.Configuration["EnvVariableHavingYourSubpath"]) which does require altering the reverse proxy to tell it to also use the subpath for the forwarded requests.

    P.S. the reason you don’t see the logs is the reverse proxy in your k8s is eating the request and not feeding them to your app.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search