• SpringCloudGateway网关整合swagger3+Knife4j3,basePath丢失请求404问题


    很多人都是照着别人的文章粘代码,我也是粘的,但是这样粘也会有问题,我搞这个Knife4j3的时候遇到两个问题,这里记录一下:
    第一个是basePath丢失,第二个解决basePath丢失完又引发了会引起application/json数据类型参数示例的问题。

    在集成 Spring Cloud Gateway 网关的时候,会出现没有 basePath 的情况,例如定义的 /jeeplus-auth、/jeeplus-system 等微服务前缀导致访问接口404:

    maven依赖:

    swagger2于17年停止维护,现在最新的版本为 Swagger3(Open Api3)

    3.0.3
    
    • 1

    在这里插入图片描述

    直接访问是找不到url的:

    在这里插入图片描述

    如果手动添加前缀是可以的

    在这里插入图片描述
    在这里插入图片描述
    但是每一个接口都要手动添加前缀太繁琐了,也失去了用swagger接口的意义;这时候我们需要在 Gateway 网关添加一个 Filter 过滤器:
    GlobalFilter : 不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。

    假设通过网关访问某一微服务的接口URL为:http://网关IP:网关端口/app-user/name/张三
    
    该服务使用了swagger3时,且通过网关访问对应接口文档,此时URL则会变为:http://网关IP:网关端口/name/张三
    
    请求会缺失bathPath(如果使用服务名动态路由的话,则实际缺失的是对应微服务的applicationName)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在网关模块添加一个过滤器SwaggerGlobalFilter:

    网关请求对应微服务接口文档资源时(/v3/api-doc),使用响应拦截,为其响应JSON中添加上对应的bathPath:
    在这里插入图片描述

    过滤器代码:

    package com.jeeplus.gateway.filter;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.parser.Feature;
    import lombok.extern.slf4j.Slf4j;
    import org.reactivestreams.Publisher;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.core.io.buffer.DataBufferUtils;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.nio.charset.Charset;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * swagger v3/api-docs缺失basePath 过滤器
     * 解决swagger3 响应缺少bathPath,请求无法动态路由到服务的问题
     * @Author 955
     * @Date 2022-09-22 11:31
     * @Description
     */
    @Slf4j
    @Component
    public class SwaggerGlobalFilter  implements GlobalFilter, Ordered{
        public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getPath().toString();
            String host = request.getLocalAddress().getHostString();
            int port = request.getLocalAddress().getPort();
            if (!path.endsWith("/v3/api-docs")) {
                return chain.filter(exchange);
            }
            String[] pathArray = path.split("/");
            System.out.println(pathArray);
            String basePath = pathArray[1];
            ServerHttpResponse originalResponse = exchange.getResponse();
            // 定义新的消息头
            ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                @Override
                public Mono writeWith(Publisher body) {
                    if (super.getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) {
                        Flux fluxBody = Flux.from(body);
                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                            List list = new ArrayList<>();
                            dataBuffers.forEach(dataBuffer -> {
                                byte[] content = new byte[dataBuffer.readableByteCount()];
                                dataBuffer.read(content);
                                DataBufferUtils.release(dataBuffer);
                                list.add(new String(content, Charset.forName("UTF-8")));
                            });
                            String s = this.listToString(list);
                            //Feature.DisableSpecialKeyDetect:禁用特殊字符检查
                            JSONObject jsonObject = JSON.parseObject(s, Feature.DisableSpecialKeyDetect);
                            jsonObject.put("host", host + ":" + port);
                            jsonObject.put("basePath", basePath);
                            s = jsonObject.toString();
                            // 设置更新后的header请求头长度
                            int length = s.getBytes().length;
                            HttpHeaders headers = originalResponse.getHeaders();
                            headers.setContentLength(length);
                            return bufferFactory().wrap(s.getBytes(Charset.forName("UTF-8")));
                        }));
                    }
                    return super.writeWith(body);
                }
    
                @Override
                public HttpHeaders getHeaders() {
                    // 获取父类原始ServerHttpResponse的header请求头信息,这是代理Delegate类型
                    HttpHeaders httpHeaders = super.getHeaders();
                    httpHeaders.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
                    return httpHeaders;
                }
    
                private String listToString(List list) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : list) {
                        stringBuilder.append(s);
                    }
                    return stringBuilder.toString();
                }
            };
    
            // replace response with decorator
            return chain.filter(exchange.mutate().response(decoratedResponse).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
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    重启再次查看发现已经正常了。

    在这里插入图片描述

    测试一下接口:

    在这里插入图片描述

    踩坑:

    如果按网上的教程只加过滤器,basePath确实可以显示出来,但是会引发新的问题:
    application/json数据类型参数示例丢失
    在这里插入图片描述

    需要在过滤器里面加上这一行:

                            //Feature.DisableSpecialKeyDetect:禁用特殊字符检查
                            JSONObject jsonObject = JSON.parseObject(s, Feature.DisableSpecialKeyDetect);
    
    • 1
    • 2

    在这里插入图片描述
    再次启动查看:
    都正常了。
    在这里插入图片描述

  • 相关阅读:
    2022第五空间WEB&MISC
    ABAP 计算时间差
    性能优化:线程快照获取与分析
    欧盟电动助力自行车标准EN15194
    LeetCode SQL专项练习(4)组合查询 & 指定选取
    第一章-数据库的概述
    拿捏红黑树(C++)
    mysql查看回滚记录
    修炼离线:(五)hbase映射表插入hive
    网关(Gateway)- 自定义过滤器工厂
  • 原文地址:https://blog.csdn.net/u010797364/article/details/133688347