Spring Boot WebFlux WebClient异步调用异常处理问题问询
WebClient异步请求合并:异常处理与状态码检测解决方案
我来帮你搞定这个WebClient异步请求的异常处理问题!咱们先拆解下你的困惑:为什么添加了onStatus却没看到异常抛出,反而block()返回null?还有为啥@ControllerAdvice没起作用?
一、为啥@ControllerAdvice没用?
首先得明确:@ControllerAdvice是用来处理Spring WebFlux控制器接收请求时抛出的异常,而WebClient是作为客户端发起请求的异常,属于Reactive流内部的错误,不在控制器的请求处理链路里,所以这些注解完全不会触发。咱们得把异常处理放在WebClient的请求流里才行。
二、正确配置WebClient的状态码与异常处理
你的onStatus方向是对的,但有两个小问题需要修正:
- 要从
ClientResponse中提取更详细的错误信息(比如响应体),而不是直接传clientResponse对象; - 确保异常被正确包装成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
相关产品推荐
相关产品推荐

