使用带有阻塞/同步请求的 Spring 的 WebClient 通过 try/catch 捕获异常

afs*_*s35 10 java rest spring spring-boot spring-webclient

我需要发出同步的阻塞请求,并且我正在使用 SpringWebClient而不是 Spring RestTemplate,因为后者已被弃用。在这种情况下,我不需要反应式功能,我只想以简单的方式使用 REST API,而不包含额外的依赖项。

\n

我有以下代码,其按预期工作:

\n
MyObject object = webClient.get()\n    .uri( myUri )\n    .retrieve()\n    .bodyToMono( MyObject.class )\n    .block()\n
Run Code Online (Sandbox Code Playgroud)\n

但是,我需要处理无法连接到 API 或连接但收到 4xx/5xx 代码时的情况。

\n

因此,最简单的方法是将调用放入 try/catch 中,并捕获 Spring 的,如果它获得 4xx/5xx 代码,WebClientResponseException则会抛出该错误:.bodyToMono

\n
import org.springframework.web.reactive.function.client.WebClientResponseException;\n\ntry {\n\n    MyObject object = webClient.get()\n        .uri( myUri )\n        .retrieve()\n        .bodyToMono( MyObject.class )\n        .block()\n\n}\n\ncatch ( WebClientResponseException e ) {\n\n    // Logic to handle the exception.\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这工作正常,但如果连接被拒绝(例如,如果 URL 错误或服务关闭),则不起作用。在这种情况下,我在控制台中看到以下内容:

\n
\n

reactor.core.Exceptions$ErrorCallbackNotImplemented:\nio.netty.channel.AbstractChannel$AnnotatedConnectException:\nfinishConnect(..) 失败: 连接被拒绝: /127.0.0.1:8090 引起\n由: io.netty.channel.AbstractChannel$AnnotatedConnectException: \nfinishConnect(..) 失败:连接被拒绝:/127.0.0.1:8090\n抑制:reactor.core.publisher.FluxOnAssembly$OnAssemblyException:在以下站点\n观察到错误:\n|_ 检查点 \xe2 \x87\xa2 请求 GET http://127.0.0.1:8090/test [DefaultWebClient] 堆栈跟踪:引起原因:java.net.ConnectException:\nfinishConnect(..) 失败:连接被拒绝\nat io.netty.channel .unix.Errors.throwConnectException(Errors.java:124)\n~[netty-transport-native-unix-common-4.1.48.Final.jar:4.1.48.Final]\n(...)

\n
\n

我不确定需要捕获哪个异常来处理这种情况。

\n

除了上述内容之外,如果连接被拒绝,我还想抛出一个自定义异常,如果收到错误代码,我还想抛出一个不同的自定义异常。对于第二种情况,我尝试使用以下.onStatus方法:

\n
try {\n\n    MyObject object = webClient.get()\n        .uri( myUri )\n        .retrieve()\n        .onStatus( HttpStatus::is4xxClientError, response -> { \n            return Mono.error( new CustomClientException( "A client error ocurred" ) );\n        })\n        .bodyToMono( MyObject.class )\n        .block()\n\n}\n\ncatch ( CustomClientException e ) {\n\n    // Logic to handle the exception.\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

但是,尽管堆栈跟踪确实出现在控制台上,但异常并未在 catch 块内捕获。

\n

有没有办法使用 try/catch 块处理 4xx/5xx 代码和连接错误,希望有自定义异常?或者我应该使用不同的网络客户端和/或改变我的方法?我不熟悉反应式编程。

\n

提前致谢。

\n

rew*_*olf 7

使用retrieve(),底层 HTTP 客户端上发生的所有异常都被包装在被RuntimeException调用的ReactiveException. 这是通过反应式接口冒泡检查异常的一种方式。

提供了一种方法来获取 中实际包装的异常Exceptions.unwrap()。然后,您可以抛出未包装的异常,并可以在适当的时候捕获它。执行此操作的一种方法可能如下:

 void makeHttpCall(..) throws Exception {
    try {
        // ..
        // webclient request code
        // ..
    } catch(Exception e) {
        throw Exceptions.unwrap(e);
    }
 }

 // somewhere else:
 try {
     makeHttpCall(..);
 } catch (ConnectException e) {
     // Do your specific error handling
 }
Run Code Online (Sandbox Code Playgroud)

我不太喜欢声明一个方法,throws Exception但目前还不知道它可能是什么。