首页 > 解决方案 > 使用 Spring WebClient 时如何捕获 IOException?

问题描述

我正在做一个项目,我们正在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
}

在重构代码时,我还必须重构测试,并且我遇到了一个测试,在该测试中,我们基本上抛出一个ConnectException以查看我们的内部代码是否根据我们的需要捕获它们。使用RestTemplate的异常类,我们可以像这样定义异常:

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

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

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

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

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

这里的方法是什么?

标签: springexceptionspring-webflux

解决方案


最好的方法几乎肯定是处理反应流本身的所有错误。服务器响应错误通常最好通过使用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
}

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

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

return webClient
    .get()
    .uri(someUri)
    .retrieve()
    .toEntity(SomeBusinessClass.class)
    .onErrorMap(IOException.class, UncheckedIOException::new)
    .block()

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

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


推荐阅读