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

WebClient错误时返回ResponseEntity而非抛异常的Java17实现方案

WebClient处理错误状态码返回ResponseEntity的解决方案

问题背景

我此前从未使用过WebClient,现需通过它向外部URL发送POST请求,需求如下:

  • 收到204状态码时正常继续流程
  • 收到错误状态码时返回包含错误信息的ResponseEntity

但当前WebClient遇到错误状态码时会直接抛出异常,无法返回预期的ResponseEntity。尝试过onStatus的多种写法,要么无效,要么只能获取状态码但无法携带错误信息;不想通过try-catch捕获WebClientResponseException(担心代码评审不通过),也不想使用已废弃的exchange()方法,寻求Java 17环境下的可行方案。

初始代码:

public ResponseEntity<Void> post(String uri, User value) {
    return webClient.post()
            .uri(uri)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(value)
            .retrieve() // 原代码拼写错误为retreive
            .toEntity(Void.class)
            .timeout(properties.getTimeout())
            .retryWhen(retry
                    .filter(this::filterError)
                    .doBefore(log.error("error message..."))) // 原代码doBefore用法有误
            .block();
}

可行解决方案

核心思路是在onStatus回调中,将错误响应转换为包含错误状态码和信息的ResponseEntity,而非抛出异常或返回空值。以下提供两种适配不同需求的实现:

方案1:保持ResponseEntity,用响应头携带错误信息

如果必须维持返回类型为ResponseEntity<Void>,可以将错误信息存入自定义响应头:

public ResponseEntity<Void> post(String uri, User value) {
    return webClient.post()
            .uri(uri)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(value)
            .retrieve()
            .onStatus(HttpStatus::isError, clientResponse -> {
                // 读取错误响应体内容
                return clientResponse.bodyToMono(String.class)
                        .map(errorMsg -> {
                            // 构造包含错误状态码和信息的ResponseEntity
                            HttpHeaders headers = new HttpHeaders();
                            headers.add("X-Error-Detail", errorMsg);
                            return ResponseEntity.status(clientResponse.statusCode())
                                    .headers(headers)
                                    .build();
                        });
            })
            .toEntity(Void.class)
            .timeout(properties.getTimeout())
            .retryWhen(retry
                    .filter(this::filterError)
                    .doBeforeRetry(signal -> log.error("准备重试,错误原因:{}", signal.failure().getMessage())))
            .block();
}

方案2:调整返回类型为ResponseEntity,用响应体携带错误信息

如果允许调整返回类型,直接将错误信息放入响应体更直观:

public ResponseEntity<String> post(String uri, User value) {
    return webClient.post()
            .uri(uri)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(value)
            .retrieve()
            .onStatus(HttpStatus::isError, clientResponse -> {
                // 读取错误响应体并直接作为ResponseEntity的body返回
                return clientResponse.bodyToMono(String.class)
                        .map(errorMsg -> ResponseEntity.status(clientResponse.statusCode()).body(errorMsg));
            })
            .toEntity(String.class)
            .timeout(properties.getTimeout())
            .retryWhen(retry
                    .filter(this::filterError)
                    .doBeforeRetry(signal -> log.error("准备重试,错误原因:{}", signal.failure().getMessage())))
            .block();
}

关键说明

  1. onStatus的正确用法:通过clientResponse.bodyToMono(String.class)读取错误响应体,再映射为目标ResponseEntity,避免抛出异常。
  2. 修正原代码问题:原代码中retreive拼写错误(应为retrieve),doBefore需改为doBeforeRetry才能正确在重试前打印日志。
  3. 重试逻辑保留:原有重试逻辑不受影响,错误状态下的ResponseEntity会正常返回,不会触发重试(除非你的filterError方法包含错误状态码的重试逻辑)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:15:26