I am upgrading a .net API to .net Core 3.1 and using Swashbuckle.AspNetcore 5.4.1. The API is running inside a ServiceFabric app. I found this https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1173 and tried to follow that and swagger gets generated but if I try to use the Swagger UI to send requests the request URL is with the wrong IP so the request fail. In the old Swashbuckle 4.0.1 setup we did not specify host, only the relative basePath. How can I achieve the same?
Startup.cs
var swaggerBasePath = "/MySfApp/SfApp.ClientApi/";
app.UseSwagger(c =>
{
c.SerializeAsV2 = serializeAsSwaggerV2;
c.RouteTemplate = "swagger/{documentName}/swagger.json";
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
swaggerDoc.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}{swaggerBasePath}" } };
});
});
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("api/swagger.json", "My API V1");
});
The result is that the Swagger UI loads correctly on URL:
http://145.12.23.1:54000/MySfApp/SfApp.ClientApi/swagger/index.html
and it says under name that BaseUrl is:
[ Base URL: 10.0.0.4:10680/MySfApp/SfApp.ClientApi/ ]
The 10.0.0.4:10680 is the node inside the ServiceFabric cluster. Correct IP to reach from outside is 145.12.23.1:54000. In the older version (4.0.1) of Swashbuckle it says baseUrl without IP first: "/MySfApp/SfApp.ClientApi"
Swagger.json is located at:
http://40.68.213.118:19081/MySfApp/SfApp.ClientApi/swagger/api/swagger.json
and it says:
"swagger": "2.0",
...
"host": "10.0.0.4:10680",
"basePath": "/MySfApp/SfApp.ClientApi/",
"schemes": [
"http"
],
"paths": {
"/activity/{activityId}": {
"get"
...etc
If i try to send a GET request from the Swagger UI the request is sent to wrong IP:
curl -X GET "http://10.0.0.4:10680/MySfApp/MySfApp/activity/3443"
EDIT 1: After some digging I have now changed the setup to this in startup.cs
var swaggerBasePath = "/MySfApp/SfApp.ClientApi/";
app.UsePathBase($"/{swaggerBasePath}");
app.UseMvc();
app.UseSwagger(c =>
{
c.SerializeAsV2 = serializeAsSwaggerV2;
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
if (!httpReq.Headers.ContainsKey("X-Original-Host"))
return;
var serverUrl = $"{httpReq.Headers["X-Original-Proto"]}://" +
$"{httpReq.Headers["X-Original-Host"]}/" +
$"{httpReq.Headers["X-Original-Prefix"]}";
swaggerDoc.Servers = new List<OpenApiServer>()
{
new OpenApiServer { Url = serverUrl }
};
});
});
app.UseSwaggerUI(options => {
options.SwaggerEndpoint("api/swagger.json", "My API V1");
});
This now leads to the Swagger UI loading properly with the baseUrl
http://145.12.23.1:54000/MySfApp/SfApp.ClientApi/swagger/index.html
and also swagger.json is served correctly with the correct baseUrl.
http://145.12.23.1:54000/MySfApp/SfApp.ClientApi/swagger/api/swagger.json
So the wrong hostname is resolved. Thanks to idea from this thread.
However when I try to call an endpoint from the Swagger UI page, the curl URL does not include the baseUrl. So closer... but currently not possible to use Swagger UI.
curl -X GET "http://10.0.0.4:10680/activity/3443"
The swagger.json does not have 'host' nor 'basePath' defined.
Launch the app, and navigate to http://localhost:<port>/swagger/v1/swagger.json . The generated document describing the endpoints appears as shown in OpenAPI specification (openapi. json). The Swagger UI can be found at http://localhost:<port>/swagger .
We're using Swashbuckle version 6.1.4
- which is the latest as of this time of writing and we're still having the same issue when our API is deployed in Azure App Service that is mapped through Azure Front Door and APIM. The "Try out" functionality does not work as the base path / api route prefix is stripped from the Swagger UI. For example,
Instead of https://{DOMAIN}.com/{BASEPATH}/v1/Foo
, the Swagger UI uses this: https://{DOMAIN}.com/v1/Foo
. You can see that the /BASEPATH
is missing.
I spent the whole day trying to fix this with trial and error, trying various approaches with no luck, I couldn't get an elegant way to get the base path from swagger configuration. For the time being, here's what I did to fix it:
app.UseSwagger(options =>
{
//Workaround to use the Swagger UI "Try Out" functionality when deployed behind a reverse proxy (APIM) with API prefix /sub context configured
options.PreSerializeFilters.Add((swagger, httpReq) =>
{
if (httpReq.Headers.ContainsKey("X-Forwarded-Host"))
{
//The httpReq.PathBase and httpReq.Headers["X-Forwarded-Prefix"] is what we need to get the base path.
//For some reason, they returning as null/blank. Perhaps this has something to do with how the proxy is configured which we don't have control.
//For the time being, the base path is manually set here that corresponds to the APIM API Url Prefix.
//In this case we set it to 'sample-app'.
var basePath = "sample-app"
var serverUrl = $"{httpReq.Scheme}://{httpReq.Headers["X-Forwarded-Host"]}/{basePath}";
swagger.Servers = new List<OpenApiServer> { new OpenApiServer { Url = serverUrl } };
}
});
})
.UseSwaggerUI(options =>
{
options.RoutePrefix = string.Empty;
options.SwaggerEndpoint("swagger/v1/swagger.json", "My Api (v1)");
});
Here's an open discussion related to this issue here.
I were having something similar in my solution and I have used a little bit this way and that works well for me, in case that helps someone.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrWhiteSpace(pathBase))
{
app.UsePathBase($"/{pathBase.TrimStart('/')}");
app.Use((context, next) =>
{
context.Request.PathBase = new PathString($"/{pathBase.TrimStart('/')}");
return next();
});
if (env.IsDevelopment())
{
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
if (!httpReq.Headers.ContainsKey("X-Original-Host"))
return;
var serverUrl = $"{httpReq.Headers["X-Original-Proto"]}://" + $"{httpReq.Headers["X-Original-Host"]}/" + $"{httpReq.Headers["X-Original-Prefix"]}";
swaggerDoc.Servers = new List<OpenApiServer>()
{
new OpenApiServer { Url = serverUrl }
}
});
});
app.UseSwaggerUI(c => c.SwaggerEndpoint($"/{pathBase.TrimStart('/')}/swagger/v1/swagger.json", "My.API v1"));
}
}
}
check the last line app.UseSwaggerUI(c => c.SwaggerEndpoint($"/{pathBase.TrimStart('/')}/swagger/v1/swagger.json", "My.API v1"));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With