如何使用Apache HttpClient记录解压后的Gzip响应内容?
解决方案
Apache HttpClient的wire日志记录的是原始传输字节,所以gzip压缩的内容会显示为乱码。要记录解压后的响应体,不能直接依赖原生wire日志,可通过以下两种方案实现:
方案一:自定义HttpClient拦截器(推荐)
给HttpClient添加拦截器,在响应自动解压后读取并记录内容:
- 修改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); }
- 调整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
相关产品推荐
相关产品推荐

