如何在使用 Spring WebClient 时捕获 IOException?

Con*_*eer 4 spring exception spring-webflux

我正在开发一个项目,我们正在从 迁移RestTemplateWebClient.

WebClient这样实现的:

try {
    ...
    return webClient
        .get()
        .uri(someUri)
        .retrieve()
        .toEntity(SomeBusinessClass.class).block()
} catch(WebClientException e) {
    // do some stuff
    // want to catch IOExceptions here as well
}
Run Code Online (Sandbox Code Playgroud)

在重构代码时,我也必须重构测试,我遇到了一个测试,在该测试中我们基本上抛出一个ConnectException来看看我们的内部代码是否根据我们的需要捕获它们。通过RestTemplate的异常类,我们能够像这样定义异常:

ResourceAccessException exc = new ResourceAccessException("I/O error on GET request", new ConnectException("Connection refused: connect"))
Run Code Online (Sandbox Code Playgroud)

WebClient我尝试对提供的异常类执行相同的操作WebClientException,但这是一个抽象类,唯一继承它的类WebClientResponseException并且不提供允许执行相同操作的构造函数。所以我唯一的选择是这样做RuntimeException

RuntimeException exc = new RuntimeException("I/O error on GET request", new ConnectException("Connection refused: connect"))
Run Code Online (Sandbox Code Playgroud)

但是,由于我不想重写我们的内部代码来捕获RuntimeException级别上的异常WebClientException,所以这不是一个选项,我想知道如何做到这一点?

我试图在 Spring 文档中找出如何IOException在使用时处理 's WebClient,但找不到任何东西。

这里的方法是什么?

Mic*_*rry 8

最好方法几乎肯定是处理反应流本身中的所有错误。服务器响应错误通常最好通过使用exchange()而不是retrieve()然后手动处理响应来处理,并且底层IOException使用onErrorResume()onErrorReturn()可用于此目的的反应性运算符。

但是,您提到您正在从阻止代码迁移,所以我知道实际上这可能(尚未)出现在卡片上。如果你想坚持捕获异常:

但由于我不想重写我们的内部代码来捕获 RuntimeException 级别的异常,而是捕获 WebClientException 级别的异常,所以这不是一个选项,我想知道如何做到这一点?

想要捕获所有传输错误WebClientException并不是一个明智的选择。正如你所说,两者都不是RuntimeException出于显而易见的原因而被抓住的。

简化它,WebClientException意味着“我连接到该 URL 并向其发送内容,没有出现任何问题,但它告诉我滚开”(即,它生成了错误代码而不是 200 响应。)

这可能是因为 404(未找到资源)、500(服务器错误)、418(您正在尝试连接到茶壶,而不是服务器)等。

IOException另一方面意味着“甚至无法建立与此 URL 的连接”。可能是因为连接被主动拒绝、域名无法解析、SSL证书过期等。

两者并不相似,这样对待它们会显得相当奇怪和令人困惑。

如果你想在同一个块中处理它们,那就没问题了——你可能会天真地这样做:

catch(WebClientException|IOException e) {
    // do some stuff
}
Run Code Online (Sandbox Code Playgroud)

...但你当然不能,因为IOException经过检查。(Java 中的反应式流不会抛出已检查异常,每个已检查异常都会映射到 a RuntimeException。)

但是,您可以将所有内容映射IOExceptionUncheckedIOException

return webClient
    .get()
    .uri(someUri)
    .retrieve()
    .toEntity(SomeBusinessClass.class)
    .onErrorMap(IOException.class, UncheckedIOException::new)
    .block()
Run Code Online (Sandbox Code Playgroud)

...然后要么执行catch(WebClientException|UncheckedIOException ex),要么在单独的 catch 块中处理它们。

这当然不是处理反应性思维异常的“好”方法,但如果您的目标是以尽可能少的更改进行迁移,那么这可能就是您所追求的。