记一次springcloud gateway记录日志响应结果乱码问题

前言

最近团队的网关日志发现有不少响应结果记录,出现形如下的乱码

�       �V*.I,IU�JK�)N�Q�M-.NL��^�m��?��(�钍/�,}�����]O7L|���ŲƧ�MϦnP�Q*K�)�*�+���QJ-*�/r�O����{�@8�    ��

一开始感觉是不是中文乱码,但是后面发现有些日志不是中文,也是乱码,而有些记录的日志又能正常显示。于是搜索了一圈,在https://blog.csdn.net/u013506626/article/details/134487673
在这篇文章找到的思路以及解决答案。

如何解决

根据上面博文介绍是因为请求的headers中加了有"Accept-Encoding"属性,值为"gzip, deflate, br",导致响应结果乱码。解决思路就是将Accept-Encoding置为空“”就可以解决,按他的思路,我就写了一个过滤器

@RequiredArgsConstructor
public class RemoveGzipHeaderGlobalFilter implements Ordered, GlobalFilter {

    private final GwCommonProperty gwCommonProperty;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (isSkipRemoveGzipHeaderEnabled(exchange)) {
            return chain.filter(exchange);
        } else {
            ServerHttpRequest request = exchange.getRequest().mutate()
                    .header(HttpHeaders.ACCEPT_ENCODING, "").build();
            return chain.filter(exchange.mutate().request(request).build());

        }
    }

    private boolean isSkipRemoveGzipHeaderEnabled(ServerWebExchange exchange){
        if(!gwCommonProperty.isRemoveGzipHeaderEnabled()){
            return true;
        }
        String gzipHeader = exchange.getRequest().getHeaders().getFirst(HttpHeaders.ACCEPT_ENCODING);

        if(!StringUtils.hasText(gzipHeader)){
            return true;
        }
        return !gzipHeader.contains("gzip");

    }

@Override
public int getOrder() {
    return Ordered.HIGHEST_PRECEDENCE;
    }
}

后面果然不再出现乱码。既然是Accept-Encoding引起的乱码问题,我们就来聊下Accept-Encoding

Accept-Encoding

Accept-Encoding 是 HTTP 协议中的一个头部字段,其主要作用在于告知服务器,客户端能够理解的内容编码方式。这个字段对于网络传输效率的优化非常重要,因为它允许服务器根据客户端的能力来压缩响应数据,从而减少传输的数据量,加快网页加载速度。

1、常见编码方式:

gzip: 使用 Lempel-Ziv 编码(LZ77)和 Huffman 编码进行压缩的算法。
deflate: 使用 zlib 库和 deflate 压缩算法进行压缩。
br(Brotli): Google 开发的一种新的数据压缩算法,旨在提供比 gzip 和 deflate 更高的压缩率。

2、字段格式:

Accept-Encoding 字段的值是一个由逗号分隔的列表,其中包含了客户端支持的内容编码方式。例如:Accept-Encoding: gzip, deflate, br

3、工作流程:

客户端在发送 HTTP 请求时,会在请求头部中包含 Accept-Encoding 字段,列出它支持的内容编码方式。

服务器在收到请求后,会检查 Accept-Encoding 字段,并根据客户端支持的内容编码方式来选择合适的压缩算法来压缩响应数据。

如果服务器选择了一种内容编码方式,它会在响应头部的 Content-Encoding 字段中指定所使用的编码方式。

网关日志记录响应结果乱码原因

介绍完Accept-Encoding,我们继续探讨一下为啥Accept-Encoding会引起网关日志响应结果乱码,因为设置了Accept-Encoding: gzip,deflate,所以服务器就会根据客户端支持的内容编码方式来选择合适的压缩算法来压缩响应,而网关层数据没对数据进行解压缩,因此就乱码

因此解决乱码的思路理论上会有2种,一种是上述博文介绍的,去掉Accept-Encoding: gzip,deflate这个头信息。去掉这个头信息就是告诉服务器,客户端不支持压缩,要求不压缩直接返回数据

另外一种思路是如果服务器选择了一种内容编码方式,它会在响应头部的 Content-Encoding 字段中指定所使用的编码方式。因此我们就可以根据Content-Encoding来判断是否要对数据进行解压缩

网关日志记录过滤器核心改造的示例如下

 /**
     * 记录响应日志
     * 通过 DataBufferFactory 解决响应体分段传输问题。
     */
  private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, AccessLog accessLog) {
        ServerHttpResponse response = exchange.getResponse();
        DataBufferFactory bufferFactory = response.bufferFactory();
        return new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    LogUtils.setReponse(accessLog,exchange);
                    Flux<? extends DataBuffer> fluxBody = Flux.from(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);


                    content = isGzip(response) ? gzipMessageBodyResolver.decode(content) : content;

                    // 释放掉内存
                    DataBufferUtils.release(join);
                    String responseResult = new String(content, StandardCharsets.UTF_8);

                    accessLog.setResponseData(responseResult);

                        return bufferFactory.wrap(content);
                    }));
                    }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
    }



    public boolean isGzip(ServerHttpResponse serverHttpResponse) {
        HttpHeaders headers = serverHttpResponse.getHeaders();
        if (headers.containsKey(HttpHeaders.CONTENT_ENCODING)) {
            List<String> encodingList = headers.get(HttpHeaders.CONTENT_ENCODING);
            return CollectionUtil.isNotEmpty(encodingList) && encodingList.contains(GZIP);
        }
        return false;
    }




注: 特别提醒,因为要获取服务端header响应Content-Encoding,用的是ServerHttpResponse,而不是ServerHttpRequest

总结

综上解决因Accept-Encoding引起的乱码方式有2种,一种是直接移除Accept-Encoding,告诉服务端不要对响应数据进行压缩,直接返回未压缩数据。另外一种如果不移除Accept-Encoding,就得根据Content-Encoding来对服务端响应的数据进行解压缩

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容