如何捕获阻塞式WebClient GET请求onStatus中的自定义异常?
我完全懂你遇到的这个坑——用WebClient做阻塞调用时,明明在onStatus里抛了自定义的PersonNotFoundException,结果外层catch块却抓不到,反而被Reactor的ReactiveException给包了一层,导致程序直接终止。这其实是Reactor框架的设计逻辑:block()方法只会抛出RuntimeException或Error,如果你的自定义异常是受检异常(继承自Exception而非RuntimeException),就会被自动包装进ReactiveException里。
下面给你几个实用的解决办法:
方法1:将自定义异常改为非受检异常(推荐)
响应式编程生态里普遍使用非受检异常,所以直接让PersonNotFoundException继承RuntimeException就行,这样Reactor就不会额外包装它了:
public class PersonNotFoundException extends RuntimeException { public PersonNotFoundException(String message) { super(message); } }
修改后你的原有客户端代码不需要任何改动,外层的catch块就能直接捕获到PersonNotFoundException了。
方法2:手动unwrap异常(如果必须保留受检异常)
如果你因为业务约束必须让PersonNotFoundException是受检异常,那可以在调用block()之后,手动提取被包装的原始异常:
public PersonDto getPersonDetails(String lastName) throws PersonNotFoundException { try { return webClient.get() .uri(personEndpoint + "/{lastName}", lastName) .retrieve() .onStatus(status -> status.equals(HttpStatus.NO_CONTENT), clientResponse -> Mono.error(new PersonNotFoundException("Person " + lastName + " Not Found"))) .bodyToMono(PersonDto.class) .block(); } catch (ReactiveException e) { // 用Reactor工具方法剥开异常包装 Throwable originalException = Exceptions.unwrap(e); if (originalException instanceof PersonNotFoundException) { throw (PersonNotFoundException) originalException; } // 非目标异常则继续抛出原包装异常 throw e; } }
这里用到的Exceptions.unwrap()是Reactor提供的工具方法,能帮你层层解开嵌套的Reactor异常,拿到最原始的自定义异常。
方法3:用blockOptional()转换空结果触发异常(另一种思路)
你也可以把204响应转换成空Mono,再通过blockOptional()的空值判断来抛出自定义异常,完全绕开Reactor的异常包装逻辑:
public PersonDto getPersonDetails(String lastName) throws PersonNotFoundException { return webClient.get() .uri(personEndpoint + "/{lastName}", lastName) .retrieve() .onStatus(status -> status.equals(HttpStatus.NO_CONTENT), clientResponse -> Mono.empty()) // 将204转为空Mono .bodyToMono(PersonDto.class) .blockOptional() .orElseThrow(() -> new PersonNotFoundException("Person " + lastName + " Not Found")); }
验证效果
修改后,你的调用代码里的catch块就能正常捕获到目标异常了:
PersonDto personDto = null; try { personDto = personServiceCLient.getPersonDetails("Smith"); } catch (PersonNotFoundException e) { // 这里就能正常执行204对应的自定义处理逻辑了 System.out.println("处理人员不存在异常:" + e.getMessage()); }
内容的提问来源于stack exchange,提问作者cjwillis48

