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