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

WebClient使用exchangeToMono获取ClientResponse时响应体为null问题

WebClient exchangeToMono响应体为null问题解决

我在单元测试中编写代码理解WebClient API时遇到问题:使用exchangeToMono获取的ClientResponse能正确返回200状态码,但响应体始终为null;而使用已废弃的exchange()方法却能正常获取响应内容"Some string response"。

测试代码如下:

@BeforeEach
public void setUpTest() throws JsonProcessingException {
    baseUrl = String.format("http://localhost:%s", mockBackEnd.getPort());
    String response = "Some string response";
    ObjectMapper objectMapper = new ObjectMapper();

    mockBackEnd.enqueue(new MockResponse()
            .setBody(objectMapper.writeValueAsString(response))
            .addHeader(HttpHeaders.CONTENT_TYPE, ContentType.TEXT_PLAIN));
}

@Test
public void test_clientResponse_return_type() {
    WebClient webClient = WebClient.builder()
            .baseUrl(baseUrl)
            .build();

    Mono<ClientResponse> clientResponseMono = webClient.post()
            .uri(baseUrl)
            .accept(MediaType.TEXT_PLAIN)
            .bodyValue("")
            .exchangeToMono(Mono::just);

    ClientResponse clientResponse = clientResponseMono.block();

    log.info("Response StatusCode is :-" + clientResponse.statusCode());
    log.info("Response Body is :-" + clientResponse.bodyToMono(String.class).block());
}

问题原因

exchangeToMono的设计初衷是让开发者在操作符内部完成响应体的消费与处理,当你直接用Mono::just返回ClientResponse时,WebClient会认为响应体未被处理,从而提前关闭响应流,导致后续调用bodyToMono无法读取到内容。

而废弃的exchange()方法将ClientResponse直接暴露到外部,允许在外部消费响应体,但这种方式存在资源泄漏风险(如未消费响应体导致连接池资源无法释放),因此Spring官方已不推荐使用。

解决方案

方案1:在exchangeToMono内部处理响应体(推荐)

直接在exchangeToMono中完成状态码日志打印与响应体读取,这是Spring官方推荐的用法:

@Test
public void test_clientResponse_return_type() {
    WebClient webClient = WebClient.builder()
            .baseUrl(baseUrl)
            .build();

    Mono<String> responseBodyMono = webClient.post()
            .uri(baseUrl)
            .accept(MediaType.TEXT_PLAIN)
            .bodyValue("")
            .exchangeToMono(response -> {
                log.info("Response StatusCode is :-" + response.statusCode());
                return response.bodyToMono(String.class);
            });

    String responseBody = responseBodyMono.block();
    log.info("Response Body is :-" + responseBody);
}

方案2:缓存响应体后返回ClientResponse

如果确实需要获取完整的ClientResponse对象,可以在exchangeToMono内部先缓存响应体,再通过mutate()方法构建新的ClientResponse:

@Test
public void test_clientResponse_return_type() {
    WebClient webClient = WebClient.builder()
            .baseUrl(baseUrl)
            .build();

    Mono<ClientResponse> clientResponseMono = webClient.post()
            .uri(baseUrl)
            .accept(MediaType.TEXT_PLAIN)
            .bodyValue("")
            .exchangeToMono(response -> 
                response.bodyToMono(String.class)
                        .map(body -> response.mutate().body(Mono.just(body)).build())
            );

    ClientResponse clientResponse = clientResponseMono.block();

    log.info("Response StatusCode is :-" + clientResponse.statusCode());
    log.info("Response Body is :-" + clientResponse.bodyToMono(String.class).block());
}

内容的提问来源于stack exchange,提问作者Mark Gargan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 03:16:10