记一次线上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.rhkb.cn/news/199423.html

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

相关文章

嵌入式QTGit面试题

自己在秋招过程中遇到的QT和嵌入式和Git相关的面试题&#xff0c;因为比较少就一起放了 QT connect第5个参数是什么&#xff1f; Qt::AutoConnection&#xff1a; 默认值&#xff0c;使用这个值则连接类型会在信号发送时决定。 如果接收者和发送者在同一个线程&#xff0c;则…

SVG圆形 <circle>的示例代码

本专栏是汇集了一些HTML常常被遗忘的知识&#xff0c;这里算是温故而知新&#xff0c;往往这些零碎的知识点&#xff0c;在你开发中能起到炸惊效果。我们每个人都没有过目不忘&#xff0c;过久不忘的本事&#xff0c;就让这一点点知识慢慢渗透你的脑海。 本专栏的风格是力求简洁…

RabbitMQ 部署及配置详解(集群部署)

单机部署请移步&#xff1a; RabbitMQ 部署及配置详解 (单机) RabbitMQ 集群是一个或 多个节点&#xff0c;每个节点共享用户、虚拟主机、 队列、交换、绑定、运行时参数和其他分布式状态。 一、RabbitMQ 集群可以通过多种方式形成&#xff1a; 通过在配置文件中列出群集节点以…

pnpm : 无法加载文件 E:\Soft\PromSoft\nodejs\node_global\pnpm.ps1,

pnpm : 无法加载文件 E:\Soft\PromSoft\nodejs\node_global\pnpm.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中 的 about_Execution_Policies。 所在位置 行:1 字符: 1pnpm -v~~~~ CategoryI…

linux高级篇基础理论五(用户安全,口令设置,JR暴力破解用户密码,NMAP端口扫描)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️不能因为人生的道路坎坷,就使自己的身躯变得弯曲;不能因为生活的历程漫长,就使求索的 脚步迟缓。 ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xff1a;云计算技…

使用Python的turtle模块绘制玫瑰花图案(含详细Python代码与注释)

1.1引言 turtle模块是Python的标准库之一&#xff0c;它提供了一个绘图板&#xff0c;让我们可以在屏幕上绘制各种图形。通过使用turtle&#xff0c;我们可以创建花朵、叶子、复杂的图案等等。本博客将介绍如何使用turtle模块实现绘制图形的过程&#xff0c;并展示最终结果。 …

【Linux】【开发】使用sed命令遇到的乱码问题

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;Linux技术&…

机器学习笔记 - Ocr识别中的CTC算法原理概述

一、文字识别 在文本检测步骤中,分割出了文本区域。现在需要识别这些片段中存在哪些文本。 机器学习笔记 - Ocr识别中的文本检测EAST网络概述-CSDN博客文章浏览阅读300次。在 EAST 网络的这个分支中,它合并了 VGG16 网络不同层的特征输出。现在,该层之后的特征大小将等于 p…

Ubuntu18.04运行gazebo的launch文件[model-4] process has died报错

启动gazebo仿真环境报错[model-4] process has died [model-4] process has died [pid 2059, exit code 1, cmd /opt/ros/melodic/lib/gazebo_ros/spawn_model -urdf -model mycar -param robot_description __name:model __log:/root/.ros/log/8842dc14-877c-11ee-a9d9-0242a…

猫12分类:使用多线程爬取图片的Python程序

本文目标 对于猫12目标检测部分的数据集&#xff0c;采用网络爬虫来制作数据集。 在网络爬虫中&#xff0c;经常需要下载大量的图片。为了提高下载效率&#xff0c;可以使用多线程来并发地下载图片。本文将介绍如何使用Python编写一个多线程爬虫程序&#xff0c;用于爬取图片…

Wireshark抓包:理解TCP三次握手和四次挥手过程

TCP是一种面向连接、端到端可靠的协议&#xff0c;它被设计用于在互联网上传输数据和确保成功传递数据和消息。本节来介绍一下TCP中的三次握手和四次挥手。 文章目录 1 TCP头部格式2 wireshark抓包分析2.1 SEQ和ACK2.2 三次握手2.3 四次挥手 3 程序 1 TCP头部格式 TCP头部占据…

Pandas+Matplotlib 数据分析

利用可视化探索图表 一、数据可视化与探索图 数据可视化是指用图形或表格的方式来呈现数据。图表能够清楚地呈现数据性质&#xff0c; 以及数据间或属性间的关系&#xff0c;可以轻易地让人看图释义。用户通过探索图&#xff08;Exploratory Graph&#xff09;可以了解数据的…

srs webrtc推拉流环境搭建(公网)

本地环境搭建 官方代码https://github.com/ossrs/srs 拉取代码&#xff1a; git clone https://github.com/ossrs/srs.gitcd ./configure make ./objs/srs -c conf/https.rtc.confsrs在公网上&#xff0c;由于srs是lite-ice端&#xff0c;导致他不会主动到srs获取自己的公网i…

七天.NET 8操作SQLite入门到实战 - SQLite 简介

什么是SQLite&#xff1f; SQLite是一个轻量级的嵌入式关系型数据库&#xff0c;它以一个小型的C语言库的形式存在。它的设计目标是嵌入式的&#xff0c;而且已经在很多嵌入式产品中使用了它&#xff0c;它占用资源非常的低&#xff0c;在嵌入式设备中&#xff0c;可能只需要几…

《崩坏:星穹铁道》1.5仙舟罗浮-绥园全宝箱攻略

大家好&#xff0c;我是闲游盒小盒子&#xff0c;本篇来说下崩铁1.5版本仙舟罗浮-绥园的全宝箱攻略&#xff0c;共有19个宝箱加1个扑满&#xff1b;做完间章可获取前14个普通宝箱加2个精英怪宝箱&#xff0c;以及1个扑满&#xff1b;完成《狐斋志异》全任务可获得另外3个宝箱。…

Visio免费版!Visio国产平替软件,终于被我找到啦!

作为一个职场人士&#xff0c;我经常需要绘制各种流程图和图表&#xff0c;而Visio一直是我使用的首选工具。但是&#xff0c;随着公司的发展和工作的需要&#xff0c;我逐渐发现了Visio的优点和不足。 首先&#xff0c;让我们来看看Visio的优点。Visio是一个专业的流程图和图…

pytho你-opencv划痕检测

pytho你-opencv划痕检测 这次实验&#xff0c;我们将对如下图片进行划痕检测&#xff0c;其实这个比较有难度&#xff0c;因为清晰度太差了。 我们做法如下&#xff1a; &#xff08;1&#xff09;读取图像为灰度图像&#xff0c;进行自适应直方图均衡化处理&#xff0c;增强…

使ros1和ros2的bag一直互通

很多文章都是先source ros1 然后source ros2,再play bag source /opt/ros/noetic/setup.bash source /opt/ros/foxy/setup.bash ros2 bag play -s rosbag_v2 kitti_raw00.bag 但实测会出问题: 为使ros1和ros2的bag一直互通 sudo apt update sudo apt install ros-foxy-ro…

从0开始学习JavaScript--JavaScript使用Promise

JavaScript中的异步编程一直是开发中的重要话题。传统的回调函数带来了回调地狱和代码可读性的问题。为了解决这些问题&#xff0c;ES6引入了Promise&#xff0c;一种更现代、更灵活的异步编程解决方案。本文将深入探讨JavaScript中如何使用Promise&#xff0c;通过丰富的示例代…

Django 路由配置(二)

一、路由 就是根据用户请求的URL链接来判断对应的出来程序&#xff0c;并返回处理结果&#xff0c;也是就是URL和django的视图建立映射关系. 二、Django请求页面的步骤 1、首先Django确定要使用的根URLconf模块&#xff0c;通过ROOT_URLCONF来设置&#xff0c;在settings.py配置…