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

Spring Boot WebFlux WebClient异步调用异常处理问题问询

WebClient异步请求合并:异常处理与状态码检测解决方案

我来帮你搞定这个WebClient异步请求的异常处理问题!咱们先拆解下你的困惑:为什么添加了onStatus却没看到异常抛出,反而block()返回null?还有为啥@ControllerAdvice没起作用?

一、为啥@ControllerAdvice没用?

首先得明确:@ControllerAdvice是用来处理Spring WebFlux控制器接收请求时抛出的异常,而WebClient是作为客户端发起请求的异常,属于Reactive流内部的错误,不在控制器的请求处理链路里,所以这些注解完全不会触发。咱们得把异常处理放在WebClient的请求流里才行。

二、正确配置WebClient的状态码与异常处理

你的onStatus方向是对的,但有两个小问题需要修正:

  1. 要从ClientResponse中提取更详细的错误信息(比如响应体),而不是直接传clientResponse对象;
  2. 确保异常被正确包装成Reactive流的错误信号,这样Mono.zip才会传播这个错误。

示例:给每个WebClient请求添加状态码处理

// 先定义自定义异常
class Data4xxException extends RuntimeException {
    public Data4xxException(String message) {
        super(message);
    }
}

class Data5xxException extends RuntimeException {
    public Data5xxException(String message) {
        super(message);
    }
}

// 配置WebClient请求的异常处理
Mono<Person> personMono = client.get()
        .uri("/person/{id}", personId)
        .retrieve()
        // 处理4xx状态码
        .onStatus(HttpStatus::is4xxClientError, clientResponse -> {
            return clientResponse.bodyToMono(String.class)
                    .flatMap(errorBody -> {
                        String errorMsg = String.format("获取Person失败,ID: %s,状态码: %s,错误信息: %s",
                                personId, clientResponse.statusCode(), errorBody);
                        return Mono.error(new Data4xxException(errorMsg));
                    });
        })
        // 处理5xx状态码
        .onStatus(HttpStatus::is5xxServerError, clientResponse -> {
            return clientResponse.bodyToMono(String.class)
                    .flatMap(errorBody -> {
                        String errorMsg = String.format("获取Person服务异常,ID: %s,状态码: %s,错误信息: %s",
                                personId, clientResponse.statusCode(), errorBody);
                        return Mono.error(new Data5xxException(errorMsg));
                    });
        })
        .bodyToMono(Person.class);

Mono<List<Hobby>> hobbiesMono = client.get()
        .uri("/person/{id}/hobbies", personId)
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, clientResponse -> {
            return clientResponse.bodyToMono(String.class)
                    .flatMap(errorBody -> {
                        String errorMsg = String.format("获取Hobbies失败,ID: %s,状态码: %s,错误信息: %s",
                                personId, clientResponse.statusCode(), errorBody);
                        return Mono.error(new Data4xxException(errorMsg));
                    });
        })
        .onStatus(HttpStatus::is5xxServerError, clientResponse -> {
            return clientResponse.bodyToMono(String.class)
                    .flatMap(errorBody -> {
                        String errorMsg = String.format("获取Hobbies服务异常,ID: %s,状态码: %s,错误信息: %s",
                                personId, clientResponse.statusCode(), errorBody);
                        return Mono.error(new Data5xxException(errorMsg));
                    });
        })
        .bodyToFlux(Hobby.class)
        .collectList();

三、让Mono.zip抛出异常而非返回null

当你用Mono.zip合并两个Mono时,如果其中任意一个Mono产生错误信号,整个zip后的Mono会直接变成错误状态。调用block()时,这个错误会被包装成RuntimeException抛出(或者你自定义的异常),而不是返回null。

如果之前你得到了null,大概率是有地方吞了异常——比如你有没有在某个地方加了onErrorReturn(null)?或者自定义异常没有正确继承RuntimeException?

正确的合并与异常捕获

try {
    Map<String, Object> data = Mono.zip(personMono, hobbiesMono, (person, hobbies) -> {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("person", person); // 注意你之前写的personName应该是person对象吧?
        map.put("hobbies", hobbies);
        return map;
    }).block();
    // 正常处理data
} catch (Data4xxException e) {
    // 处理4xx客户端错误
    log.error("客户端请求错误", e);
} catch (Data5xxException e) {
    // 处理5xx服务端错误
    log.error("服务端响应错误", e);
} catch (RuntimeException e) {
    // 处理其他Reactive流异常
    log.error("请求合并异常", e);
}

四、额外:处理204无内容的情况

如果你需要单独处理204(No Content)状态,可以在onStatus里单独判断:

.onStatus(HttpStatus.NO_CONTENT::equals, clientResponse -> {
    String errorMsg = String.format("获取Person无内容,ID: %s", personId);
    return Mono.error(new Data4xxException(errorMsg));
})

这样配置后,只要任意一个请求返回4xx/5xx/204,对应的异常就会被抛出,block()不会再返回null,你可以在catch块里捕获并处理这些异常啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:02:47