Spring Boot3.2.2与Apache HttpClient5下Logbook拦截器测试异常修复方法
修复Spring Boot 3.2.2 + Logbook 3.5.0 下的 UnsupportedOperationException 问题
核心原因
Spring Framework 6.1重构了HttpComponentsClientHttpRequest,原有的EntityUtils.toByteArray()方式会触发不可重复读取的流抛出UnsupportedOperationException,而Logbook 3.5.0尚未适配这个变更。
解决方案
1. 优先升级Logbook版本
Logbook在3.6.0及后续版本中已经修复了与Spring Framework 6.1的兼容问题,直接升级依赖即可:
- Maven:
<dependency> <groupId>org.zalando</groupId> <artifactId>logbook-spring-boot-starter</artifactId> <version>3.7.0</version> </dependency>
- Gradle:
implementation 'org.zalando:logbook-spring-boot-starter:3.7.0'
升级后无需修改业务代码,即可解决流读取的兼容性问题。
2. 临时适配(无法升级Logbook时)
如果暂时不能升级Logbook,需要修改自定义MDC处理逻辑,避免直接读取HttpEntity的字节数组:
- 自定义
ClientHttpRequestInterceptor时,替换EntityUtils.toByteArray(entity)的逻辑,改用HttpEntity.writeTo()将内容写入临时输出流,同时保留MDC上下文填充:
@Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // 原有MDC填充逻辑保留 MdcService.fillContext(request); // 替换EntityUtils.toByteArray的逻辑 if (request instanceof HttpEntityRequest) { HttpEntity<?> entity = ((HttpEntityRequest) request).getHttpEntity(); if (entity != null && entity.getBody() != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); entity.writeTo(baos); // 后续如果需要使用请求体,用baos.toByteArray() } } return execution.execute(request, body); }
- 若使用Logbook的自定义配置,需覆盖
LogbookHttpRequest的内容读取逻辑,确保使用writeTo而非直接读取字节数组。
3. 测试环境临时方案
针对测试场景,可以:
- 使用
MockRestServiceServer模拟HTTP请求,绕过真实的HttpComponentsClientHttpRequest实例; - 自定义一个可重复读取的
HttpEntity包装类,包装原请求实体,让EntityUtils.toByteArray()能正常获取内容:
public class RepeatableHttpEntity<T> extends HttpEntity<T> { private final byte[] content; public RepeatableHttpEntity(T body, HttpHeaders headers) throws IOException { super(body, headers); if (body instanceof InputStreamSource) { this.content = EntityUtils.toByteArray((HttpEntity<T>) this); } else { this.content = null; } } @Override public void writeTo(OutputStream outputStream) throws IOException { if (content != null) { outputStream.write(content); } else { super.writeTo(outputStream); } } }
在测试中使用该类包装请求实体,避免流读取异常。
内容的提问来源于stack exchange,提问作者user27265443
相关产品推荐
相关产品推荐

