记一次解决 RestTemplate 和 HttpClient 请求结果乱码的问题

背景

最近在对接一个第三方接口时,遇到了一个比较烦人的问题 ------ 请求结果是乱码。

起初以为是字符编码的问题,但调试了一圈才发现:返回的数据其实是经过压缩的,而客户端并没有正确地解压。本文记录下整个排查过程以及最终的解决方法,供以后再遇到类似问题时参考。

问题现象

使用 RestTemplate 或 Apache 的 HttpClient 调用某个接口时,返回的内容看起来像是一堆乱码,具体形式大概是这样:

txt 复制代码
����(����+/HI,ITHI,I-.Q�(��/J

最初怀疑是编码问题,比如对 UTF-8 内容用了 ISO-8859-1 去解码,尝试调整字符集也没解决。

排查过程

打开抓包工具一看,发现响应头中多了个字段:

http 复制代码
Content-Encoding: gzip

这就有点意思了。原来服务器压缩了响应内容,但客户端没有自动解压。进一步看请求头:

http 复制代码
Accept-Encoding: gzip, deflate

这说明我们明确告诉服务器:我支持 gzip,然后我们又没处理 gzip,这不是自找麻烦吗?

最终解决方案

解决方法有两个,选择其一即可。

方案一:干脆不接受压缩,删掉 Accept-Encoding

既然客户端不会处理压缩,那就别告诉服务器我们能接受压缩。

RestTemplate 示例:

java 复制代码
HttpHeaders headers = new HttpHeaders();
// 删除 Accept-Encoding 请求头
headers.remove(HttpHeaders.ACCEPT_ENCODING);

HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
String body = response.getBody();

HttpClient 示例:

java 复制代码
HttpGet request = new HttpGet(url);
// 删除 Accept-Encoding 请求头
request.removeHeaders("Accept-Encoding");

HttpResponse response = httpClient.execute(request);
String body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

这个方案简单粗暴,只是要注意一点:如果响应数据比较大,没压缩可能会带来带宽上的浪费。

方案二:保留压缩支持,但手动解压响应

如果希望保留压缩以提高传输效率,那就得自己处理 gzip 的解压逻辑了。

** 示例:**

java 复制代码
    /**
     * 根据响应头判断是否 GZIP 并自动解压,返回解压后的字符串
     *
     * @param headers 响应头(可为 null)
     * @param body    响应体字节数组(可能是 gzip 压缩过的)
     * @return 解压后的字符串,或原始内容
     * @throws IOException 解压异常
     */
    public static String decodeIfGzip(HttpHeaders headers, byte[] body) throws IOException {
        boolean isGzip = headers != null && "gzip".equalsIgnoreCase(headers.getFirst("Content-Encoding"));

        if (isGzip) {
            try (GZIPInputStream gzipIn = new GZIPInputStream(new ByteArrayInputStream(body));
                 ByteArrayOutputStream out = new ByteArrayOutputStream()) {
                byte[] buffer = new byte[1024];
                int len;
                while ((len = gzipIn.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
                return out.toString(StandardCharsets.UTF_8.name());
            }
        } else {
            return new String(body, StandardCharsets.UTF_8);
        }
    }

小结

总结一下,这次遇到的乱码问题其实本质上不是编码问题,而是压缩没解开的锅。

两种解决方式:

  • 不接受压缩(简单直接)
  • 支持压缩,手动解压(更灵活)

看项目需求选择方案即可。总之,别盲目排查编码了,先看看是不是被 gzip 了再说。希望这篇文章能帮你少踩一个坑。

个人简介

👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.

🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。

🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。

💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。

🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

📖 保持关注我的博客,让我们共同追求技术卓越。

相关推荐
猿究院_夏燊14 小时前
Spring Bean 生命周期
java·后端·spring
CCblandy16 小时前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
我是人✓16 小时前
SpringBoot项目的五种创建方式
java·spring boot·后端
念念清晰17 小时前
000-OpenJDK17 源码构建:MacOS环境(M芯片)
java·后端
来福猿18 小时前
SpringCloud Config 源码分析:配置拉取、刷新与多环境管理
后端·spring·spring cloud
IT_陈寒20 小时前
React组件莫名重渲染?你可能忘了这个小细节
前端·人工智能·后端
用户98883218717420 小时前
给 .NET 网络调用设定“预算”:用 CancellationTokenSource.CancelAfter 处理超时取消
后端
清风儿32920 小时前
一块不会说话的电表,把我们折腾了半个月
后端
预知同行20 小时前
深入解析 MoE 混合专家模型:从 Router 到 All-to-All 的架构全景与生产实践
后端·架构
upala20 小时前
【仓颉语言入门 · 第二篇 第 8 课】
后端