• gateway网关聚合knife4j文档,同时兼容swagger2与swagger3


    基于前两篇文章,进行整合

    springcloud-gateway 聚合swagger3请求接口丢失appliactionName解决

    springcloud-gateway聚合knife4j接口文档

    为何要兼容?微服务开发者有的使用了swagger2版本,有的使用了swagger3版本,但暴露外部给前端使用的,均统一走网关处

    兼容核心点:swagger3 返回缺少bathPath

    网关配置

    image-20221201105700475

    @Data
    @ConfigurationProperties(prefix = MyGateWayProperties.PREFIX)
    public class MyGateWayProperties {
    
        public static final String PREFIX = "gateway";
    
        /**
         * swagger3 服务列表
         */
        private List<String> swagger3Set;
    
        /**
         * 请求前缀
         */
        private String apiPrefix;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    请求文档的SwaggerResourcesProvider 进行调整,带上自定义网关请求前缀与 swagger文档后缀

    @RestController
    @RequestMapping("/swagger-resources")
    public class SwaggerHandler {
    
        @Autowired(required = false)
        private SecurityConfiguration securityConfiguration;
    
        @Autowired(required = false)
        private UiConfiguration uiConfiguration;
    
        private final SwaggerResourcesProvider provider;
    
        @Autowired
        public SwaggerHandler(SwaggerResourcesProvider provider) {
            this.provider = provider;
        }
    
        @GetMapping("/configuration/security")
        public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
            return Mono.just(new ResponseEntity<>(
                    Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),
                    HttpStatus.OK));
        }
    
        @GetMapping("/configuration/ui")
        public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
            return Mono.just(new ResponseEntity<>(
                    Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
        }
    
        @GetMapping
        public Mono<ResponseEntity<List<SwaggerResource>>> swaggerResources() {
            return Mono.just((new ResponseEntity<>(provider.get(), HttpStatus.OK)));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    /**
     * @author lei
     * @create 2022-05-25 10:01
     * @desc 网关处获取所有服务swagger文档
     **/
    @Slf4j
    @Component
    @Primary
    public class SwaggerProvider implements SwaggerResourcesProvider {
    
        /**
         * swagger2默认的url后缀
         */
        public static final String V3_API_DOCS = "/v3/api-docs";
        public static final String V2_API_DOCS = "/v2/api-docs";
    
    
        /**
         * 路由加载器
         */
        private final RouteLocator routeLocator;
        private final MyGateWayProperties myGateWayProperties;
    
        /**
         * 网关应用名称
         */
        @Value("${spring.application.name}")
        private String gateway;
    
        @Autowired
        public SwaggerProvider(RouteLocator routeLocator, MyGateWayProperties myGateWayProperties) {
            this.routeLocator = routeLocator;
            this.myGateWayProperties = myGateWayProperties;
        }
    
        @Override
        public List<SwaggerResource> get() {
            List<SwaggerResource> resources = new ArrayList<>();
            List<String> routeHosts = new ArrayList<>();
            Flux<Route> routes = routeLocator.getRoutes();
            // 获取所有可用的host,因为是注册表服务调用,所以实际是服务名
            routes.filter(route -> route.getUri().getHost() != null)
                    .filter(route -> !gateway.equals(route.getUri().getHost()))
                    .subscribe(route -> routeHosts.add(route.getUri().getHost()));
    
            // 去重下拉列表相同服务名文档,多个服务展示一个,当网关调用这个接口时,会自动通过负载均衡寻找服务host
            Set<String> docServerSet = new HashSet<>();
            routeHosts.forEach(instance -> {
                String url;
                if (myGateWayProperties.getSwagger3Set().contains(instance)) {
                    // 拼接url. /serviceId/v3/api-docs
                    url = myGateWayProperties.getApiPrefix() + "/" + instance.toLowerCase() + V3_API_DOCS;
                } else {
                    // 拼接url. /serviceId/v2/api-docs
                    url = myGateWayProperties.getApiPrefix() + "/" + instance.toLowerCase() + V2_API_DOCS;
                }
                if (!docServerSet.contains(url)) {
                    docServerSet.add(url);
                    SwaggerResource swaggerResource = new SwaggerResource();
                    swaggerResource.setUrl(url);
                    swaggerResource.setName(instance);
                    resources.add(swaggerResource);
                }
            });
            return resources;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    网关添加全局过滤器,发现以访问swagger3后缀的url则重新返回体,携带上bathPath

    /**
     * @author lei
     * @create 2022-05-26 14:36
     * @desc swagger过滤器
     * 解决swagger3 响应缺少bathPath,请求无法动态路由到服务的问题
     **/
    @Log4j2
    @Component
    public class SwaggerGlobalFilter implements GlobalFilter, Ordered {
    
        public static final String BASE_PATH = "basePath";
    
        private final MyGateWayProperties myGateWayProperties;
    
        public SwaggerGlobalFilter(MyGateWayProperties myGateWayProperties) {
            this.myGateWayProperties = myGateWayProperties;
        }
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            String path = exchange.getRequest().getPath().toString();
            if (!path.endsWith(SwaggerProvider.V3_API_DOCS)) {
                return chain.filter(exchange);
            }
            path = path.replace(myGateWayProperties.getApiPrefix(), "");
            String[] pathArray = path.split("/");
            String basePath = pathArray[1];
            ServerHttpResponse originalResponse = exchange.getResponse();
            ServerHttpResponseDecorator newResponse = new ServerHttpResponseDecorator(originalResponse) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    if (!Objects.equals(getStatusCode(), HttpStatus.OK)) {
                        return super.writeWith(body);
                    }
                    if (body instanceof Flux) {
                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                            List<String> list = new ArrayList<>();
                            dataBuffers.forEach(dataBuffer -> {
                                byte[] content = new byte[dataBuffer.readableByteCount()];
                                dataBuffer.read(content);
                                DataBufferUtils.release(dataBuffer);
                                list.add(new String(content, StandardCharsets.UTF_8));
                            });
                            String jsonStr = listToString(list);
                            JSONObject jsonObject = JSON.parseObject(jsonStr);
                            jsonObject.put(BASE_PATH, myGateWayProperties.getApiPrefix() + "/" + basePath);
                            jsonStr = jsonObject.toString();
                            return bufferFactory().wrap(jsonStr.getBytes());
                        }));
                    }
    
                    return super.writeWith(body);
                }
    
                @Override
                public HttpHeaders getHeaders() {
                    HttpHeaders httpHeaders = new HttpHeaders();
                    httpHeaders.putAll(super.getHeaders());
                    //由于修改了请求体的body,导致content-length长度不确定,因此使用分块编码
                    httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                    return httpHeaders;
                }
    
                private String listToString(List<String> list) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : list) {
                        stringBuilder.append(s);
                    }
                    return stringBuilder.toString();
                }
            };
            return chain.filter(exchange.mutate().response(newResponse).build());
        }
    
    
        @Override
        public int getOrder() {
            return -2;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    网关获取到文档访问列表

    image-20221201110418429

    访问具体服务的文档,触发全局过滤器

    image-20221201110720238

    改写响应数据,访问swagger3文档,带上了bathPath

    image-20221201110923634

    image-20221201111821666

    访问swagger2文档时,不受影响

    image-20221201111130320

  • 相关阅读:
    【Linux 】向Shell脚本传递参数、getopts、getopt
    AI人脸检测智能分析网关算法模型管理,支持自由组合算法
    Java知识点整理 13 — Hutool工具库
    String、StringBuilder、StringBuffer区别
    WPF入门教程系列三十 ——DataGrid验证
    关于 XSS 漏洞的测试
    怎样基于VitePress(Vite官网主题)写自己文档
    wxpython主目录
    【Java 简洁初始化类】匿名内部类和实例初始化块
    七日算法先导(三)—— 快速排序,插入排序
  • 原文地址:https://blog.csdn.net/leilei1366615/article/details/128124334