当我有自定义基本 URL 时,如何使用 swagger 设置 Swashbuckle v5?

问题描述 投票:0回答:4

我正在将 .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”。

swagger .net-core-3.1 swashbuckle.aspnetcore
4个回答
4
投票

我们正在使用 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)");
});

这里有与此问题相关的公开讨论这里


1
投票

我的解决方案中有类似的东西,我用了一点这种方式,这对我来说效果很好,以防对某人有帮助。

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


0
投票

试试这个:

serverUrl = $"{httpReq.Headers["X-Forwarded-Proto"]}://" +
                $"{httpReq.Headers["X-Forwarded-Host"]}" + _basePath;
其中 _basePath 可以使用 StatelessServiceContext 的 ServiceName 属性进行设置。 请注意,X-Forwarded-Proto 的原始值可能会被 SF 覆盖。


0
投票

这是我基于 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 相对于代理路由

© www.soinside.com 2019 - 2024. All rights reserved.