使用 Spring 的 WebClient 和 blocking/syncronous 请求捕获 try/catch 的异常

Catch exceptions with try/catch using Spring's WebClient with blocking/syncronous request

我需要发出一个同步的、阻塞的请求,我正在使用Spring的WebClient而不是Spring' s RestTemplate 因为后者被弃用了。在这种情况下,我不需要响应式功能,我只想以一种直接的方式使用 REST API,而不包括额外的依赖项。

我有以下代码,可以正常工作:

MyObject object = webClient.get()
    .uri( myUri )
    .retrieve()
    .bodyToMono( MyObject.class )
    .block()

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

所以,直接的方法是将调用放在 try/catch 中,然后捕获 Spring 的 WebClientResponseException,它由 .bodyToMono 抛出,如果它得到一个 4xx/5xx 代码:

import org.springframework.web.reactive.function.client.WebClientResponseException;

try {

    MyObject object = webClient.get()
        .uri( myUri )
        .retrieve()
        .bodyToMono( MyObject.class )
        .block()

}

catch ( WebClientResponseException e ) {

    // Logic to handle the exception.

}

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

reactor.core.Exceptions$ErrorCallbackNotImplemented: io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: /127.0.0.1:8090 Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: /127.0.0.1:8090 Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s): |_ checkpoint ⇢ Request to GET http://127.0.0.1:8090/test [DefaultWebClient] Stack trace: Caused by: java.net.ConnectException: finishConnect(..) failed: Connection refused at io.netty.channel.unix.Errors.throwConnectException(Errors.java:124) ~[netty-transport-native-unix-common-4.1.48.Final.jar:4.1.48.Final] (...)

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

除此之外,我还想在连接被拒绝时抛出一个自定义异常,在收到错误代码时抛出一个不同的自定义异常。在第二种情况下,我尝试使用 .onStatus 方法:

try {

    MyObject object = webClient.get()
        .uri( myUri )
        .retrieve()
        .onStatus( HttpStatus::is4xxClientError, response -> { 
            return Mono.error( new CustomClientException( "A client error ocurred" ) );
        })
        .bodyToMono( MyObject.class )
        .block()

}

catch ( CustomClientException e ) {

    // Logic to handle the exception.

}

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

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

提前致谢。

使用 retrieve(),底层 HTTP 客户端上发生的所有异常都包装在名为 ReactiveExceptionRuntimeException 中。这样做是为了通过反应接口冒泡已检查的异常。

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
 }

我不太喜欢用 throws Exception 声明一个方法,但在这一点上它根本不知道它可能是什么。