• Swashbuckle.AspNetCore_Swagger_5.6.3版本组件中的 "坑"_baseurl多了端口号


    先说解决方案吧:升级相关Swagger版本由5.6.3到6.0.0以上版本即可,我这里升级到了6.3.0

    新版本 Swashbuckle swagger 组件中的 Servers 坑

    Intro

    上周做了公司的项目升级,从 2.2 更新到 3.1, swagger 直接更新到了最新,swagger 用的组件是 Swashbuckle.AspNetCore,然后遇到一个 swagger 的问题, 在本地测试是没问题的,但是部署在测试环境之后就会有问题,主要是 swagger 界面会多一个 servers 的选项,可能会导致 swagger 不能正常使用,下面详细介绍一下

    Swagger "bug" reproduce

    大概的问题是这样的,在本地环境是好的,在测试环境部署是有问题,测试环境部署之后的 swagger 界面大致如下:

    很明显这个 servers 是有问题的,我们实际访问的地址是 https://testserver/swagger 这样的地址,但是 swagger 内部拼出来的 server 地址和实际访问的地址是不符的,swagger 生成的 open api 文档里也会有一个 servers 的属性,示例如下:

    这会导致我们使用 swagger 调试 API 的时候会走一个错误的 server 地址,实际请求的地址是 sever 地址加上 api path,可以看一个示例

    Dig the Source

    Swashbuckle.AspNetCore 是开源的,我们就是扒一扒它的实现源码吧,我们用的是 5.6.3 版本,直接看 5.6.3 tag 对应的代码,可以找到 swagger 的中间件

    https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs

    在这里我们可以看到,再返回给客户端之前 open api 文档响应之前我们是可以看到,是会经过 PreSerializeFilters 处理的,我们再详细看一下 swaggerProvider.GetSwagger 的实现

    实现代码在这里(可以通过服务注册找到对应的实现,也可以直接找对应接口的实现)

    https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L31

    二者结合来看,servers 会根据用户请求来获取一个 server 地址,而当有 X-Forwarded-Host 请求头的时候如果没有按照 swagger 指定的规则这样进行请求头的转发就会导致有问题,而我们的测试环境也正是因为如此,测试环境有一层 LB,经过 LB 转发了 X-Forwarded-Host 和 X-Forwarded-Proto 请求头,但是没有转发 X-Forwarded-Port 所以经过 swagger 的处理之后,就从 https://testserver 变成了 https://testserver:80 这样

    private string GetHostOrNullFromRequest(HttpRequest request)
    {
        if (!request.Headers.TryGetValue("X-Forwarded-Host", out StringValues forwardedHost))
            return null;
     
        var hostBuilder = new UriBuilder($"http://{forwardedHost[0]}");
     
        if (request.Headers.TryGetValue("X-Forwarded-Proto", out StringValues forwardedProto))
            hostBuilder.Scheme = forwardedProto[0];
     
        if (request.Headers.TryGetValue("X-Forwarded-Port", out StringValues forwardedPort))
            hostBuilder.Port = int.Parse(forwardedPort[0]);
     
        return hostBuilder.Uri.ToString().Trim('/');
    }
     
    private string GetBasePathOrNullFromRequest(HttpRequest request)
    {
        var pathBuilder = new StringBuilder();
     
        if (request.Headers.TryGetValue("X-Forwarded-Prefix", out StringValues forwardedPrefix))
            pathBuilder.Append(forwardedPrefix[0].TrimEnd('/'));
     
        if (request.PathBase.HasValue)
            pathBuilder.Append(request.PathBase.Value.TrimEnd('/'));
     
        return (pathBuilder.Length > 0)
            ? pathBuilder.ToString()
            : null;
    }

    解决方案

    从上面的源码中基本就可以分析出问题的原因来,解决的办法我觉得有下面几种:

    1. LB 转发的时候带上 X-Forwarded-Port 请求头,转发原始请求的端口号(需要 LB 转发自己能够控制,我们如果要配置还需要让 DevOps 的童鞋帮忙弄,如果完全是自己控制的就比较方便【推荐】)

    2. 在使用 Swagger 中间件之前把 X-Forwarded-Port 请求头设置为 443(不够灵活,如果访问 LB 是 http 或者有特别的端口号就会有问题)

    3. 在使用 swagger 中间件之前把 X-Forwarded-Host 请求头移除掉,这样就不会有 servers 这个属性了(感觉不够优雅)

    4. 注册一个 PreSerializeFilter 把 Servers 清空,实现代码如下(【推荐】,没有 servers 属性的时候完全按请求 swagger 的 baseUrl 来作为 api 的前缀,示例代码如下)

    app.UseSwagger(c =>
    {
        c.PreSerializeFilters.Add((doc, _) =>
        {
            doc.Servers?.Clear();
        });
    });

    更新之后就没有 servers 属性了,和之前的版本保持一致了

    More

    我们使用的是 5.6.3 版本,应该从 5.6.0 开始都有这个问题,如果遇到了这个问题不要慌哈,参考上面的解决方案即可

    我觉得 swagger 这样的实现方式不太友好,更好的实现应该结合微软的 ForwardHeaders 中间件来实现,Swagger 组件作者表示已经有计划,打算在 6.0 的时候更新结合微软的中间件来实现,详细可以参考 Github 上的 Issue https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1814

    Reference

    • https://github.com/domaindrivendev/Swashbuckle.AspNetCore

    • https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs

    • https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L31

    • https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1814

    本文转载自: https://blog.csdn.net/sd7o95o/article/details/109476290

  • 相关阅读:
    linux拷贝软连接文件
    【知识点】Java常用类库
    Maven之pom.xml配置文件详解
    Java+Bigdata学习路线
    Hadoop界的Hello World!
    JavaEE三大框架的整合
    学完微型服务器(Tomcat)对其工作流程的理解,自己着手写个简单的tomcat
    【数据结构与算法】之链表
    JavaEE项目开发所需要的包(Struts2+Spring5+Hibernate5)
    在Linux(Centos7)系统上对进行Hadoop分布式配置以及运行Hadoop伪分布式实例
  • 原文地址:https://www.cnblogs.com/lxhbky/p/16136621.html
Copyright © 2020-2023  润新知