我正在将 .net API 升级到 .net Core 3.1 并使用 Swashbuckle.AspNetcore 5.4.1。该 API 在 ServiceFabric 应用程序内运行。我发现了这个 https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1173 并尝试遵循它并生成了 swagger,但是如果我尝试使用 Swagger UI 发送请求,则请求 URL 是错误的所以请求失败的IP。 在旧的 Swashbuckle 4.0.1 设置中,我们没有指定主机,仅指定相对基本路径。我怎样才能达到同样的效果?
启动.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");
});
结果是 Swagger UI 在 URL 上正确加载:
http://145.12.23.1:54000/MySfApp/SfApp.ClientApi/swagger/index.html
名称下显示 BaseUrl 是:
[ Base URL: 10.0.0.4:10680/MySfApp/SfApp.ClientApi/ ]
10.0.0.4:10680是ServiceFabric集群内的节点。从外部访问的正确 IP 是 145.12.23.1:54000。在 Swashbuckle 的旧版本(4.0.1)中,它首先显示没有 IP 的 baseUrl:“/MySfApp/SfApp.ClientApi”
Swagger.json 位于:
http://40.68.213.118:19081/MySfApp/SfApp.ClientApi/swagger/api/swagger.json
它说:
"swagger": "2.0",
...
"host": "10.0.0.4:10680",
"basePath": "/MySfApp/SfApp.ClientApi/",
"schemes": [
"http"
],
"paths": {
"/activity/{activityId}": {
"get"
...etc
如果我尝试从 Swagger UI 发送 GET 请求,该请求将发送到错误的 IP:
curl -X GET "http://10.0.0.4:10680/MySfApp/MySfApp/activity/3443"
编辑1: 经过一番挖掘后,我现在已将设置更改为 启动.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");
});
现在,Swagger UI 可以使用 baseUrl 正确加载
http://145.12.23.1:54000/MySfApp/SfApp.ClientApi/swagger/index.html
并且 swagger.json 也使用正确的 baseUrl 正确提供。
http://145.12.23.1:54000/MySfApp/SfApp.ClientApi/swagger/api/swagger.json
这样错误的主机名就解决了。 感谢此线程的想法。
但是,当我尝试从 Swagger UI 页面调用端点时,curl URL 不包含 baseUrl。如此接近...但目前无法使用 Swagger UI。
curl -X GET "http://10.0.0.4:10680/activity/3443"
swagger.json 没有定义“host”或“basePath”。
我们正在使用 Swashbuckle 版本
6.1.4
- 这是撰写本文时的最新版本,当我们的 API 部署在通过 Azure Front Door 和 APIM 映射的 Azure 应用服务中时,我们仍然遇到同样的问题。 “试用”功能不起作用,因为基本路径/api 路由前缀已从 Swagger UI 中剥离。例如,
Swagger UI 使用的是:
https://{DOMAIN}.com/{BASEPATH}/v1/Foo
,而不是 https://{DOMAIN}.com/v1/Foo
。您可以看到缺少 /BASEPATH
。
我花了一整天的时间试图通过反复试验来解决这个问题,尝试了各种方法但没有运气,我无法找到一种优雅的方法来从 swagger 配置中获取基本路径。目前,这是我修复它的方法:
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)");
});
这里有与此问题相关的公开讨论这里。
我的解决方案中有类似的东西,我用了一点这种方式,这对我来说效果很好,以防对某人有帮助。
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"));
}
}
}
检查最后一行 app.UseSwaggerUI(c => c.SwaggerEndpoint($"/{pathBase.TrimStart('/')}/swagger/v1/swagger.json", "My.API v1"));
试试这个:
serverUrl = $"{httpReq.Headers["X-Forwarded-Proto"]}://" +
$"{httpReq.Headers["X-Forwarded-Host"]}" + _basePath;
其中 _basePath 可以使用 StatelessServiceContext 的 ServiceName 属性进行设置。
请注意,X-Forwarded-Proto 的原始值可能会被 SF 覆盖。
这是我基于 Vincent Maverick Durano 答案的版本。 这个想法是可移植的,而不需要对路径进行硬编码。我采用浏览器发送的 Referer 标头并使用该 url 作为基础。这是假设您的应用程序配置为在 API 的根目录中包含 index.html。
当您直接托管应用程序时,它会类似于
https://example.com/index.html
,UI API 调用类似于 https://example.com/v1/users
。
当您使用路由通过 Azure Front Door 等代理托管应用程序时,您可能需要类似
https://anotherexample.com/myapp/index.html
的内容,整个 https://example.com/
被代理为 https://anotherexample.com/myapp/
,因此索引随之而来,因此 UI API 调用为 https://anotherexample.com/myapp/v1/users
代码
app.UseSwagger(options =>
{
//Workaround to use the Swagger UI "Try Out" functionality when deployed behind a reverse proxy with routing
options.PreSerializeFilters.Add((swagger, httpReq) =>
{
if (httpReq.Headers.ContainsKey("Referer"))
{
//The assumption is being made here that index.html is hosted in the root of a virtual directory where a route can be a root
//example without a proxy
//https://example.com/index.html <-- the API is directly accesible at domain root like example.com/v1/users
//
//example with a proxy and routing
//https://example.com/appx/index.html <-- the API is accesible at example.com/appx "root" like example.com/appx/v1/users
//everything should be relative to example.com/appx
var basePath = httpReq.Headers["Referer"].ToString().Replace("index.html", "");
swagger.Servers = new List<OpenApiServer> { new OpenApiServer { Url = basePath } };
}
});
});
以及带有相对路径和版本控制的 swagger UI 注册
app.UseSwaggerUI(c =>
{
// build a swagger endpoint for each discovered API version, and show latest as first
for (var i = versionProvider.ApiVersionDescriptions.Count - 1; i >= 0; i--)
{
var description = versionProvider.ApiVersionDescriptions[i];
c.SwaggerEndpoint($"swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
c.RoutePrefix = string.Empty;
});
如果您不支持版本控制,那么唯一重要的一行是
c.SwaggerEndpoint($"swagger/
通知 $"swagger
开始时没有 /
以使 UI 相对于代理路由