gateway 返回Body 數據居然出現偶現的亂碼問題, 原來是數據被截斷背的鍋

1、查看問題

在開發的時候,發現頁面的數據偶先亂碼,但有不清楚是那的問題,我們使用swagger-ui參數,發現亂碼數據,但概率爲1/10,每10次大概查詢一次,如下
在這裏插入圖片描述
一會又是正常的
在這裏插入圖片描述
返回數據是json數據,編碼 utf8,一切都是正常的,重點是偶現,這就奇葩了
在這裏插入圖片描述

2、定位問題

因爲我們是微服務項目使用了 spring-cloud-gateway做爲網關,於是使用網關地址和 項目地址分別測試

走網關地址
在這裏插入圖片描述
走具體的項目地址
在這裏插入圖片描述
果然走網關地址的url 會出現亂碼,走具體的項目地址的不會出現亂碼問題
與是就想到了是不是返回參數被截斷了,中文字符是2-3個字節,返回的參數太多,gateway會分段處理數據
當前代碼如下:----> 不知道大家能不能看出來有什麼問題


    @Autowired
    private AuthProperties authProperties;
    @Autowired
    private  GatewayLogConfig gatewayLogConfig;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    Mono<Void> voidMono = super.writeWith(fluxBody.map(dataBuffer -> {
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        //釋放掉內存
                        DataBufferUtils.release(dataBuffer);
                        //原數據,想修改、查看就隨意而爲了
                        String data = new String(content, Charset.forName("UTF-8"));
                        // 不對html 頁面進行過濾
                        // if (data.indexOf("<html>") == -1) {   // }
                        //xss過濾
                        if (authProperties.isXssRespAttack()) {
                            data = HtmlEncodeUtil.htmlEncode(data);
                        }
                        //byte[] uppedContent = new String(data, Charset.forName("UTF-8")).getBytes();
                        return bufferFactory.wrap(data.getBytes());
                    }));
                    // 打印日誌, 注意,body數據在exchange 只能讀取一次
                    gatewayLogConfig.putLog(exchange, "響應成功");
                    return voidMono;
                }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
        // replace response with decorator
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

這個方法如果數據過多太長 super.writeWith(fluxBody.map(dataBuffer -> { 會多次進入,數據會被拆成字節在拼接在一起返回前端,中文是2到3字節,如果剛好把這個中文的拆成兩半,一個字的上半部分字節在第一次,下半部分字節在第二次,這樣返回的數據在拼接在一起被拆了的字節無法還原成一箇中文漢字,就會出現亂碼的問題了

3、處理問題

  • 亂碼問題其實就是返回數據gateway 進行了截斷所產生的,所以我們需要把返回的組進行join上就不會產生亂碼的,如果還是亂碼,請根據自己系統的實際編碼進行轉換即可完美解決,

  • 請注意:這裏使用的是 DefaultDataBufferFactory 的join方法去合併多個dataBuffers ↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 最大的區別

修改代碼如下,重點部分

 super.writeWith(fluxBody.buffer().map(dataBuffers -> {

       DataBuffer join = dataBufferFactory.join(dataBuffers);
       byte[] content = new byte[join.readableByteCount()];
       join.read(content);
       DataBufferUtils.release(join);
       String responseData = new String(content, Charsets.UTF_8);

當前完整代碼如下

    @Autowired
    private AuthProperties authProperties;
    @Autowired
    private GatewayLogConfig gatewayLogConfig;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        try {
            ServerHttpResponse originalResponse = exchange.getResponse();
            DataBufferFactory bufferFactory = originalResponse.bufferFactory();
            ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    if (body instanceof Flux) {
                        Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                            DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                            DataBuffer join = dataBufferFactory.join(dataBuffers);
                            byte[] content = new byte[join.readableByteCount()];
                            join.read(content);
                            DataBufferUtils.release(join);
                            String responseData = new String(content, Charsets.UTF_8);
                            if (log.isDebugEnabled()) {
                                log.debug("響應轉前:{}", responseData);
                            }
                            responseData = responseData.replaceAll(":null", ":\"\"");
                            if (log.isDebugEnabled()) {
                                log.debug("響應轉後:{}", responseData);
                            }
                            byte[] uppedContent = responseData.getBytes(Charsets.UTF_8);
                            // 不對html 頁面進行過濾
                            // if (data.indexOf("<html>") == -1) {   // }
                            // 判斷是否爲swagger文檔,v2/api-docs  ,是不進行xss過濾
                            if (responseData.toString().indexOf("v2/api-docs") == -1 && authProperties.isXssRespAttack()) {
                                responseData = HtmlEncodeUtil.htmlEncode(responseData);
                            }
                            // 注意,body數據在exchange 只能讀取一次
                            gatewayLogConfig.putLog(exchange, "響應成功");
                            return bufferFactory.wrap(uppedContent);
                        }));
                    } else {
                        return chain.filter(exchange);
                    }
                }
            };
            // replace response with decorator
            return chain.filter(exchange.mutate().response(decoratedResponse).build());
        } catch (Exception e) {
            log.error(" ReplaceNullFilter 異常", e);
            return chain.filter(exchange);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章