You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Apache HttpClient记录解压后的Gzip响应内容?

解决方案

Apache HttpClient的wire日志记录的是原始传输字节,所以gzip压缩的内容会显示为乱码。要记录解压后的响应体,不能直接依赖原生wire日志,可通过以下两种方案实现:

方案一:自定义HttpClient拦截器(推荐)

给HttpClient添加拦截器,在响应自动解压后读取并记录内容:

  1. 修改RestTemplate配置,自定义HttpClient并注入拦截器:
@Bean
public RestTemplate restTemplate() {
    // 创建HttpClient并添加响应拦截器
    CloseableHttpClient httpClient = HttpClientBuilder.create()
            .addInterceptorFirst(new HttpResponseInterceptor() {
                @Override
                public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
                    // 仅在debug级别开启时执行记录
                    if (LoggerFactory.getLogger(getClass()).isDebugEnabled()) {
                        HttpEntity entity = response.getEntity();
                        if (entity != null) {
                            // 读取解压后的响应体
                            String body = EntityUtils.toString(entity, StandardCharsets.UTF_8);
                            // 输出日志
                            LoggerFactory.getLogger(getClass()).debug("解压后的响应体: {}", body);
                            // 重置实体,保证后续流程正常读取
                            response.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
                        }
                    }
                }
            })
            .build();

    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
    return new RestTemplate(requestFactory);
}
  1. 调整Logback配置,给拦截器所在包开启debug级别:
<logger name="org.apache.hc.client5.http" level="debug"/>
<!-- 替换为你拦截器实际所在的包路径 -->
<logger name="com.your.project.config" level="debug"/>

方案二:使用Spring请求拦截器

通过RestTemplate添加ClientHttpRequestInterceptor,在响应返回后读取解压内容:

@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getInterceptors().add((request, body, execution) -> {
        ClientHttpResponse response = execution.execute(request, body);
        // 读取解压后的响应体
        String responseBody = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
        // 记录日志
        LoggerFactory.getLogger(getClass()).debug("解压后的响应体: {}", responseBody);
        // 包装响应体,避免流关闭后无法重复读取
        return new BufferingClientHttpResponseWrapper(response);
    });
    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
    return restTemplate;
}

// 辅助类:实现响应体缓存,支持重复读取
static class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
    private final ClientHttpResponse response;
    private byte[] body;

    BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
        this.response = response;
    }

    @Override
    public InputStream getBody() throws IOException {
        if (body == null) {
            body = StreamUtils.copyToByteArray(response.getBody());
        }
        return new ByteArrayInputStream(body);
    }

    // 其余方法直接委托给原响应对象
    @Override
    public HttpHeaders getHeaders() { return response.getHeaders(); }
    @Override
    public HttpStatus getStatusCode() throws IOException { return response.getStatusCode(); }
    @Override
    public int getRawStatusCode() throws IOException { return response.getRawStatusCode(); }
    @Override
    public String getStatusText() throws IOException { return response.getStatusText(); }
    @Override
    public void close() { response.close(); }
}

注意事项

  • HttpClient默认已启用自动解压逻辑,拦截器中读取的内容是已解压后的明文。
  • 生产环境建议关闭debug级别的响应体日志,或添加日志过滤规则,避免性能损耗及敏感信息泄露。

内容的提问来源于stack exchange,提问作者Michal Krasny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 11:37:43