如何使用WebClient Filter转换Spring声明式HTTP客户端的响应体?
Spring WebClient Filter 响应体拆包问题解决
问题描述
我在使用Spring声明式HTTP客户端时,对WebClient的用法存在困惑,尝试通过Filter转换响应体,移除响应中包裹业务数据的"data"属性冗余JSON结构,但编写的代码无法编译,且调整后出现超时错误。
我的代码如下:
private ExchangeFilterFunction responseUnwrapper() { return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { if (clientResponse.statusCode().is2xxSuccessful()) { return clientResponse .bodyToMono(DataDTO.class) .map(DataDTO::data) .flatMap(mappedResponse -> Mono.just(ClientResponse.create(clientResponse.statusCode()) .headers(httpResponse -> clientResponse.headers().asHttpHeaders()) .body(mappedResponse) .build())); } else { return Mono.just(clientResponse); } }); }
编译报错是因为ClientResponse.Builder的body方法只支持三种参数类型:
Builder body(Function<Flux<DataBuffer>, Flux<DataBuffer>> transformer); Builder body(Flux<DataBuffer> body); Builder body(String body);
改成.body(mappedResponse.toString())后,又出现IllegalStateException: Timeout on blocking read for 5000000000 NANOSECONDS错误。
补充:DataDTO定义如下:
public record DataDTO<T>(T data) {}
解决方案
问题出在你直接把Java对象传给body方法,但ClientResponse.Builder不支持直接传入POJO,得把对象序列化成DataBuffer流或者合法的JSON字符串,同时要避开阻塞操作。
正确代码实现
你可以用Spring自动配置的ObjectMapper,把业务对象序列化成DataBuffer流,再传给body方法:
// 注入Spring自动配置的ObjectMapper private final ObjectMapper objectMapper; private ExchangeFilterFunction responseUnwrapper() { return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { if (clientResponse.statusCode().is2xxSuccessful()) { return clientResponse.bodyToMono(DataDTO.class) .map(DataDTO::data) .flatMap(data -> { // 把业务对象转成字节数组,再包装成DataBuffer流 Flux<DataBuffer> dataBufferFlux = Mono.fromCallable(() -> objectMapper.writeValueAsBytes(data)) .map(bytes -> clientResponse.bufferFactory().wrap(bytes)) .flux(); // 构建新的响应,保留原响应的状态码和头信息 return Mono.just(ClientResponse.create(clientResponse.statusCode()) .headers(headers -> headers.addAll(clientResponse.headers().asHttpHeaders())) .body(dataBufferFlux) .build()); }); } else { return Mono.just(clientResponse); } }); }
关键说明
- 避免直接用toString():
mappedResponse.toString()生成的不是合法JSON,后续WebClient反序列化时会卡住,触发超时——因为它在等待正确的JSON数据,但拿到的是对象的默认toString输出。 - 用ObjectMapper序列化:借助Spring自动配置的
ObjectMapper序列化对象,能保证和WebClient默认的JSON处理逻辑一致,避免格式不兼容问题。 - 保留原响应元数据:通过
headers(headers -> headers.addAll(...))把原响应的头信息完整复制到新响应中,不会丢失Cookie、Content-Type等必要信息。
替代方案(无需Filter)
如果只是少数接口需要拆包,也可以不用Filter,直接在声明式客户端方法中指定返回类型,配合@JsonRootName或者自定义Jackson反序列化器,但Filter更适合全局统一处理所有响应的拆包逻辑。
内容的提问来源于stack exchange,提问作者Eduardo Bueno
相关产品推荐
相关产品推荐

