12.4.4 处理错误
到目前为止,所有的 WebClient 示例都假设了一个圆满的结局;请求不会返回任何 400 或 500 级别的 HTTP 状态码。当这些错误状态返回时,WebClient 应该会记录失败日志;否则,静悄悄地忽略它。
如果需要处理此类错误,则可以使用对 onStatus()
的调用来指定如何处理各种 HTTP 状态码。onStatus()
接受两个函数:一个 Predicate 函数,用于匹配 HTTP 状态。另一个函数给定 ClientResponse 对象,返回 Mono<Throwable>
。
为了演示如何使用 onStatus()
创建自定义错误处理程序,考虑以下通过 ID 获取 Ingredient 的用法:
Mono<Ingredient> ingredientMono = webClient
.get()
.uri("http://localhost:8080/ingredients/{id}", ingredientId)
.retrieve()
.bodyToMono(Ingredient.class);
只要 ingredientId 中的值与已知的资源匹配,那么生成的 Mono 将在订阅时发布返回 Ingredient 对象。但是,如果没有匹配的 Ingredient 对象,那会发生什么事情呢?
当订阅 Mono 或 Flux 可能会发生错误时,注册一个出错处理函数是很重要的。就像在调用 subscribe()
方法时注册数据处理函数一样:
ingredientMono.subscribe(
ingredient -> {
// handle the ingredient data
...
},
error-> {
// deal with the error
...
});
如果找到了 Ingredient 资源对象,则在 subscribe()
方法中指定的第一个 lambda(数据处理函数)会被调用。如果没有找到资源对象,请求会用状态码 http 404 (NOT FOUND)
进行响应,这样会给第二个 lambda(错误处理函数)默认传入一个 WebClientResponseException 对象。
WebClientResponseException 对象的最大问题是它的非特异性,它无法说明具体是什么出了问题导致 Mono 出错。它只表明 WebClient 发出的请求,在响应时存在错误。您需要进一步深入 WebClientResponseException 才能知道具体出了什么问题。在任何情况下,给错误处理函数一个具体的异常值才更好,而不是给一个特定于 WebClient 的异常。
通过添加自定义的错误处理函数,您可以自主地将返回的出错状态码转换成一个Throwable。假设您想要让一个失败的 Ingredient 请求,返回一个异常 UnknownIngredientException,您可以在调用 retrieve()
之后添加对 onStatus()
的调用:
Mono<Ingredient> ingredientMono = webClient
.get()
.uri("http://localhost:8080/ingredients/{id}", ingredientId)
.retrieve()
.onStatus(HttpStatus::is4xxClientError,
response -> Mono.just(new UnknownIngredientException()))
.bodyToMono(Ingredient.class);
onStatus()
函数调用中的第一个参数是 Predicate 类型的 HttpStatus,如果是要处理的状态码,则返回 true。如果状态码匹配,则响应将会传给第二个参数指定的函数,以进一步进行处理。最终返回一个 Throwable 类型的 Mono。
在本例中,如果状态码是 400 级别的状态码(例如,客户端错误),则会返回一个带有 UnknownIngredientException 的 Mono。这会导致 ingredientMono 因该异常而失败。
请注意,HttpStatus::is4xxClientError
是对 HttpStatus
中 is4xxClientError
方法的引用。这个方法将在给定 HttpStatus
对象时被实际调用。如果需要,可以使用 HttpStatus
上的其他方法作为方法引用;或者您提供自己的 lambda 或返回布尔值的其他方法引用。
例如,您可以在错误处理函数中更具体的检查处理 HTTP 404 (NOT FOUND)
,这可以通过对 onStatus()
的调用更改来实现,就像这样:
Mono<Ingredient> ingredientMono = webClient
.get()
.uri("http://localhost:8080/ingredients/{id}", ingredientId)
.retrieve()
.onStatus(status -> status == HttpStatus.NOT_FOUND,
response -> Mono.just(new UnknownIngredientException()))
.bodyToMono(Ingredient.class);
另外值得注意的是,可以根据需要调用任意多个 onStatus()
,以处理响应中可能返回的各种 HTTP 状态码。