1. 简介

在本文中,我们将探讨如何通过 RestTemplate 发送包含压缩数据的 HTTP 请求。

同时,我们也会介绍如何配置 Spring Web 应用,使其能够正确处理客户端发来的压缩请求体。

这在高吞吐、大数据量的微服务通信中非常实用——能显著减少网络传输开销。但⚠️要注意:不是所有服务器都支持接收压缩请求体,盲目发送会导致 400 或解析失败。

2. 发送压缩请求

要实现请求体压缩,核心思路是:
✅ 在 RestTemplate 发出请求前,拦截并压缩 body
✅ 添加正确的 Content-EncodingAccept-Encoding 头部

2.1 压缩工具方法

先写一个简单的 GZIP 压缩工具方法:

public static byte[] compress(byte[] body) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) {
        gzipOutputStream.write(body);
    }
    return baos.toByteArray();
}

这个方法接收原始字节数组,返回 GZIP 压缩后的字节流。

2.2 自定义拦截器

接下来实现 ClientHttpRequestInterceptor,用于在请求发出前修改内容:

public class CompressingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest req, byte[] body, ClientHttpRequestExecution exec)
            throws IOException {
        HttpHeaders httpHeaders = req.getHeaders();
        httpHeaders.add(HttpHeaders.CONTENT_ENCODING, "gzip");
        httpHeaders.add(HttpHeaders.ACCEPT_ENCODING, "gzip");
        return exec.execute(req, compress(body));
    }
}

关键点说明:

  • Content-Encoding: gzip:告诉服务端,请求体是 GZIP 压缩过的
  • Accept-Encoding: gzip:表示客户端也接受压缩响应(可选,视需求而定)
  • ⚠️ 必须调用 compress(body) 对原始 body 进行压缩后再传递

2.3 注册到 RestTemplate

最后将拦截器注册到 RestTemplate 实例中:

@Bean
public RestTemplate getRestTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getInterceptors().add(new CompressingClientHttpRequestInterceptor());
    return restTemplate;
}

这样,所有通过该 RestTemplate 发出的请求都会自动压缩 body 并带上对应 header。

💡 小贴士:如果你使用的是 WebClient(响应式栈),可以用 ExchangeFilterFunction 实现类似功能,原理一致。

3. 服务端处理压缩请求

光客户端压缩还不够 ❌。默认情况下,Spring Boot 内嵌的 Tomcat、Netty 等服务器不会自动解压请求体,直接接收会报错或解析为空。

目前只有 JettyUndertow 支持自动解压 GZIP 请求体。

所以你必须满足两个条件:

  1. 使用 Jetty 或 Undertow 作为 Web 服务器
  2. 正确配置 handler 启用请求解压

3.1 配置 Jetty 服务器

首先在 pom.xml 中排除默认的 Tomcat,引入 Jetty:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

然后配置 GzipHandler 并启用 inflateBufferSize

@Bean
public JettyServletWebServerFactory jettyServletWebServerFactory() {
    JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
    factory.addServerCustomizers(server -> {
        GzipHandler gzipHandler = new GzipHandler();
        gzipHandler.setInflateBufferSize(1);  // ⚠️ 必须大于0才能启用解压
        gzipHandler.setHandler(server.getHandler());

        HandlerCollection handlerCollection = new HandlerCollection(gzipHandler);
        server.setHandler(handlerCollection);
    });
    return factory;
}

⚠️ 注意:setInflateBufferSize(1) 是关键!
Jetty 默认不开启请求体解压,必须显式设置该值 > 0 才会激活 inflate 功能。

3.2 配置 Undertow 服务器

同样,先切换为 Undertow:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

然后添加 RequestEncodingHandler 来处理 GZIP 编码:

@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
    UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    factory.addDeploymentInfoCustomizers((deploymentInfo) -> {
        deploymentInfo.addInitialHandlerChainWrapper(handler -> new RequestEncodingHandler(handler)
          .addEncoding("gzip", GzipStreamSourceConduit.WRAPPER));
    });
    return factory;
}

这样 Undertow 就会在请求进入 Servlet 前自动解压 GZIP 请求体,你的 Controller 可以像处理普通请求一样读取参数。

4. 总结

本文完整演示了 Spring 生态下实现请求压缩的两端配置:

角色 关键点
客户端 使用 ClientHttpRequestInterceptor 压缩 body + 添加 Content-Encoding
服务端 必须使用 Jetty/Undertow,并配置对应 Handler 启用解压

✅ 推荐场景:

  • 内部微服务之间通信(可控环境)
  • 数据上报、日志收集等大 payload 场景

❌ 不推荐场景:

  • 面向第三方或浏览器的公开接口(兼容性差)
  • 小数据量请求(压缩反而增加 CPU 开销)

📌 最后提醒:永远不要向不支持压缩的服务器发送压缩请求,否则只会换来一个 400 Bad Request 或静默失败。建议通过配置项控制是否开启压缩,便于灰度和回滚。


原始标题:How to Compress Requests Using the Spring RestTemplate | Baeldung