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

如何使用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);
        }
    });
}

关键说明

  1. 避免直接用toString():mappedResponse.toString()生成的不是合法JSON,后续WebClient反序列化时会卡住,触发超时——因为它在等待正确的JSON数据,但拿到的是对象的默认toString输出。
  2. 用ObjectMapper序列化:借助Spring自动配置的ObjectMapper序列化对象,能保证和WebClient默认的JSON处理逻辑一致,避免格式不兼容问题。
  3. 保留原响应元数据:通过headers(headers -> headers.addAll(...))把原响应的头信息完整复制到新响应中,不会丢失Cookie、Content-Type等必要信息。

替代方案(无需Filter)

如果只是少数接口需要拆包,也可以不用Filter,直接在声明式客户端方法中指定返回类型,配合@JsonRootName或者自定义Jackson反序列化器,但Filter更适合全局统一处理所有响应的拆包逻辑。

内容的提问来源于stack exchange,提问作者Eduardo Bueno

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 05:33:16