记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码

       基于公司的业务需求,在SpringCloud Gateway组件的基础上,写了一个转发服务,测试开发阶段运行正常,并实现初步使用。但三个月后,PostMan请求接口,返回异常,经排查,从日志中获取到转发响应的结果为乱码:

      

跟踪日志:

转发到目标接口,响应结果已乱码。一般排查的思路是,查看请求方和响应方的编码格式是否一致,打印请求方的编码格式为UTF-8,响应服务的编码格式也是UTF-8。

以上说明编码格式没有问题。上网去找“gateway响应结果乱码”的相关文章,大多数会提供解决方案:

DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
// 释放掉内存
DataBufferUtils.release(join);
String str = new String(content, Charset.forName("UTF-8"));
originalResponse.getHeaders().setContentLength(str.getBytes().length);
System.out.println(str);
return bufferFactory.wrap(str.getBytes());

这段关键代码,在我的响应结果包装过滤器是有的,如下:

    /*** 获取到解码方的response,验签--->重新封装--->加签* 通过 DataBufferFactory 解决响应体分段传输问题。*/private ServerHttpResponseDecorator verifyRePackageSignatureResponse(ServerWebExchange exchange, String jmf_decode_url, String route_privateKey, String jmf_publicKey) {ServerHttpResponse response = exchange.getResponse();log.debug("R:给响应结果response设置编码格式----START----");response.getHeaders().add("Content-Type","application/json;charset=UTF-8");log.debug("R:给响应结果response设置编码格式-----END-----");DataBufferFactory bufferFactory = response.bufferFactory();return new ServerHttpResponseDecorator(response) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {if (body instanceof Flux) {// 获取响应类型,如果是 json 就打印String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);log.debug("响应类型为originalResponseContentType:{}",originalResponseContentType);if (RequestResponseUtil.isJson(originalResponseContentType)) {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);// 释放掉内存DataBufferUtils.release(join);// 正常返回的数据String rootData = new String(content, StandardCharsets.UTF_8);log.debug("R:正常返回的数据rootData为:{}", rootData);//使用枚举 + 工厂 + 策略模式(第二版)String newQqtBodyJson = null;try {newQqtBodyJson = DecoderSignStrategyContext.zwmVerifySignResponse(rootData, jmf_decode_url, route_privateKey, jmf_publicKey);log.debug("R:【码路由服务】经具体策略处理,得到结果newQqtBodyJson为:{}", newQqtBodyJson);} catch (Exception e) {log.error("R:【码路由服务】解码方返回结果验签异常:{}", e);throw new VerifySignException(StaticVar.FAIL_10020015, "R:【码路由服务】解码方返回结果验签异常");}byte[] respData = newQqtBodyJson.getBytes();//byte[] respData = newQqtBodyJson.getBytes(StandardCharsets.UTF_8);byte[] uppedContent = new String(respData, Charset.forName("UTF-8")).getBytes();return bufferFactory.wrap(uppedContent);}));} else {log.error("响应结果异常");throw new ProcessHandleException(StaticVar.FAIL_10020015, "R:【码路由服务】解码方返回结果异常,非法JSON");}}// if body is not a flux. never got there.return super.writeWith(body);}};}

因此不是代码的问题。又找到了一篇文章,解决了PostMan请求的问题。

https://bbs.csdn.net/topics/399102026/close

如上所述,在PostMan请求的headers中去掉Accept-Encoding,请求成功:

       至此,PostMan请求乱码的问题已解决。但事情似乎没有那么简单,接下来,使用手机模拟扫码,请求码路由服务,又出现了乱码,扫码结果页面,返回结果:

 查看日志,乱码如下:

      排查问题进入瓶颈期,有些烦躁了,必须冷静下来,重新捋一下代码,同时也在想,既然和PostMan请求存在同样的问题,是不是在请求头中默认会有一个Accept-Encoding属性,从而导致了乱码,根据这个思路,在自定义的请求转发过滤器中,发现了以下代码:

由于在请求转发的逻辑中,重新构建了一个request请求的同时,重新创建了一个请求头headers,它在重新构建的时候默认会有一个accept-encoding属性,看到这一行代码时,正印证了以上我的猜想,问题已经找到了,将其置为空字符串即可(注意:不能使其为null,也不能remove这个属性,会报错)。

  将headers的accept-encoding属性置为空字符串,并添加注释如下:

// 定义新的消息头HttpHeaders headers = new HttpHeaders();//System.out.println("headers = " + headers); //查看重新定义消息头的内容headers.putAll(exchange.getRequest().getHeaders());// 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度int length = bodyStr.getBytes().length;log.debug("bodyStr长度为:{}",length);headers.remove(HttpHeaders.CONTENT_LENGTH);// 设置CONTENT_TYPEif (StringUtils.isNotBlank(contentType)) {headers.set(HttpHeaders.CONTENT_TYPE, contentType);}String jmf_decode_url = request.getURI().toString();String qqtBodyJsonStr = JSONUtils.toString(bizPackage);//request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(bodyStr.length())); //报错JSON parse error JsonEOFException,长度必须为字节的长度request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(length));//成功,解决JSON parse error JsonEOFException/*** 乱码现象:请求响应结果乱码* 解决过程分为PostMan请求和手机端App扫码请求:*   (1)、PostMan请求响应乱码解决:*             PostMan请求的headers中默认会有"Accept-Encoding"属性,值为"gzip, deflate, br",导致响应结果乱码*         去掉Accept-Encoding后请求正常。**   (2)、手机端App扫码请求乱码解决:*             在HttpRequestSignForwardGatewayFilter中定义新的消息头,headers中默认会有"accept-encoding"属性,*         值为"gzip, deflate, br", 添加代码"request.mutate().header("accept-encoding",""); "解决乱码**       注意: 创建headers对象默认会生成“accept-encoding=‘gzip, deflate, br‘ ”属性,此处必须将accept-encoding*       置为空字符串(置为null会报错),否则使用默认值会导致响应结果乱码。*       说明: 测试开发阶段未发生此问题,第三方检测时演示出现此问题,这个可能是gateway内部的问题,尚未可知。**          经测试,这种方式已解决了:手机端APP扫码和PostMan响应结果乱码的问题(PostMan请求时可以不用刻意去掉Accept-Encoding,*       也可请求成功)**/request.mutate().header("accept-encoding","");

重启后,部署后问题得到解决。这个问题在测试开发阶段没有暴露出来,按理说应该早暴露了,但现实情况就是这么诡异,这可能是Gateway内部的bug,尚未可知。

     希望此文对遇到同样问题的小伙伴有所帮助和启发,望了解gateway内部原理机制的大神,参与讨论。

    乱码问题已排查并处理结束,完结,撒花!

参考文章:

RestTemplate请求头accept-encoding导致乱码_resttemplate 乱码_AE86Jag的博客-CSDN博客

https://bbs.csdn.net/topics/399102026/close

在此感谢文章作者!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/148897.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开启数据库审计 db,extended级别或os级别)并将审计文件存放到/opt/oracle/audit/下

文章目录 1、登录到数据库2、查看审计状态3、创建审计目录4、启用审计5、设置审计文件路径6、再次查看结果 1、登录到数据库 使用SQL*Plus或者其他Oracle数据库客户端登录到数据库。 sqlplus / as sysdba;2、查看审计状态 show parameter audit;目前是DB状态&#xff0c;并且…

【算法萌新闯力扣】:旋转字符串

力扣热题&#xff1a;796.旋转字符串 开篇 今天下午刷了6道力扣算法题&#xff0c;选了一道有多种解法的题目与大家分享。 题目链接:796.旋转字符串 题目描述 代码思路 完全按照题目的要求&#xff0c;利用StringBuffer中的方法对字符串进行旋转&#xff0c;寻找相同的一项 …

第 372 场 LeetCode 周赛题解

A 使三个字符串相等 求三个串的最长公共前缀 class Solution { public:int findMinimumOperations(string s1, string s2, string s3) {int n1 s1.size(), n2 s2.size(), n3 s3.size();int i 0;for (; i < min({n1, n2, n3}); i)if (!(s1[i] s2[i] && s2[i] s…

[工业自动化-23]:西门子S7-15xxx编程 - 软件编程 - 西门子PLC人机界面交互HMI功能概述、硬件环境准备、软件环境准备

目录 一、什么是人机界面 二、什么是PLC人机交互界面HMI 三、人机界面设计的功能列表 四、开发主机与PLC的连接方式 五、开发主机与HMI的连接方式 六、HMI组态 一、什么是人机界面 人机界面是指人与机器或系统之间的交互界面。它是人类与计算机或其他设备之间进行信息交换…

NSSCTF第13页(2)

[HNCTF 2022 Week1]Challenge__rce 提示?hint 访问看到了源码 <?php error_reporting(0); if (isset($_GET[hint])) { highlight_file(__FILE__); } if (isset($_POST[rce])) { $rce $_POST[rce]; if (strlen($rce) < 120) { if (is_string($rce…

Java Web——JavaScript基础

1. 引入方式 JavaScript程序不能独立运行&#xff0c;它需要被嵌入HTML中&#xff0c;然后浏览器才能执行 JavaScript 代码。 通过 script 标签将 JavaScript 代码引入到 HTML 中&#xff0c;有3种方式&#xff1a; 1.1. 内嵌式(嵌入式) 直接写在html文件里&#xff0c;用s…

系列六、GC垃圾回收【四大垃圾算法-标记清除算法】

一、概述 标记清除算法分为两个阶段&#xff0c;即&#xff1a;标记和清除两个阶段&#xff0c;先标记出要回收的对象&#xff0c;然后统一回收这些对象。形如&#xff1a; 老年代一般是由标记清除或者标记清除 标记压缩的混合实现。 二、原理 用通俗的话解释一下标记清除算法…

2023年中职“网络安全“—Web 渗透测试②

2023年中职“网络安全“—Web 渗透测试② Web 渗透测试任务环境说明&#xff1a;1.访问http://靶机IP/web1/,获取flag值&#xff0c;Flag格式为flag{xxx}&#xff1b;2.访问http://靶机IP/web2/,获取flag值&#xff0c;Flag格式为flag{xxx}&#xff1b;3.访问http://靶机IP/web…

SOME/IP 协议介绍(六)接口设计的兼容性规则

接口设计的兼容性规则&#xff08;信息性&#xff09; 对于所有序列化格式而言&#xff0c;向较新的服务接口的迁移有一定的限制。使用一组兼容性规则&#xff0c;SOME / IP允许服务接口的演进。可以以非破坏性的方式进行以下添加和增强&#xff1a; • 向服务中添加新方法 …

莹莹API管理系统源码附带两套模板

这是一个API后台管理系统的源码&#xff0c;可以自定义添加接口&#xff0c;并自带两个模板。 环境要求 PHP版本要求高于5.6且低于8.0&#xff0c;已测试通过的版本为7.4。 需要安装PHPSG11加密扩展。 已测试&#xff1a;宝塔/主机亲测成功搭建&#xff01; 安装说明 &am…

新生儿奶藓:原因、科普和注意事项

引言&#xff1a; 新生儿奶藓是一种常见的婴儿皮肤问题&#xff0c;通常在生后的头几个月内出现。尽管奶藓对婴儿的健康没有太大影响&#xff0c;但了解其原因、科普相关信息以及采取适当的注意事项是帮助父母更好地照顾婴儿皮肤的关键。本文将深入探讨新生儿奶藓的原因、相关…

PPT基础入门

目录 相关设置快捷键shift 快捷键Ctrl 快捷键Ctrl Shift 组合快捷键快捷键总结 相关设置 设置撤回次数 自动保存 图片压缩 字体嵌入&#xff1a;目的是在不同的电脑上保留已经设置好的字体 多格式导出 &#xff08;1&#xff09;可以导出PDF &#xff08;2&#xff09;可以导…

DevExpress中文教程 - 如何在macOS和Linux (CTP)上创建、修改报表(上)

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 DevExpress Reports — 跨平台报表组件&#x…

7.docker运行redis容器

1.准备redis的配置文件 从上一篇运行MySQL容器我们知道&#xff0c;需要给容器挂载数据卷&#xff0c;来持久化数据和配置&#xff0c;相应的redis也不例外。这里我们以redis6.0.8为例来实际说明下。 1.1 查找redis的配置文件redis.conf 下面这个网址有各种版本的配置文件供…

C++ 十进制与十六进制转换

文章作者&#xff1a;里海 来源网站&#xff1a;里海C\C专栏 十进制与十六进制转换 #include <iostream> #include <string> using namespace std;//十进制整数转十六进制字符串 string DecimalToHex(long long decimal) {string hex "";while (de…

svg图标最简单的使用方式

svg图标最简单的使用方式 使用svg图标1. 复制图标的svg代码2. 新建个存放svg图标的目录&#xff0c;新建.vue文件3. 在<template>标签内粘贴svg的代码4. 在代码中也可以调整颜色和大小5. 在组件中引用6. svg做的图标要独占一行,使用布局将它安排到合适的地方 使用svg图标…

Leetcode经典题目之“双指针交换元素“类题目

1 LC 27. 移除元素 class Solution {public int removeElement(int[] nums, int val) {int nnums.length;int s0;for(int i0;i<n;i){// 只有不等于目标值的时候才会进行交换&#xff0c;然后移动s指针if(nums[i]!val){swap(nums,i,s);}}return s;}void swap(int[]nums, int…

Uniapp连接iBeacon设备——实现无线定位与互动体验(理论篇)

目录 前言&#xff1a; 一、什么是iBeacon技术 二、Uniapp连接iBeacon设备的准备工作 硬件设备&#xff1a; 三、Uniapp连接iBeacon设备的实现步骤 创建Uniapp项目&#xff1a; 四、Uniapp连接iBeacon设备的应用场景 室内导航&#xff1a; 五、Uniapp连接iBeacon设备的未来…

X12学习手册

EDI术语中的X12是指ANSI X12 报文标准&#xff08;EDI Document Standard&#xff09;&#xff0c;于 1979 年发布&#xff0c;由认证标准委员会维护&#xff0c;在过去的几十年中得到扩展&#xff0c;以满足全球业务流程的要求&#xff0c;包括汽车、物流、零售、医药、金融、…

七,vi和vim

Linux系统会内置vi文本编辑器 Vim具有程序编辑的能力&#xff0c;可以看做是Vi的增强版本&#xff0c;可以主动的以字体颜色辨别语法的正确性&#xff0c;方便程序设计。代码补完、编译及错误跳转等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用。 vi和vim常用的三…