11.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 是对 HttpStatusis4xxClientError 方法的引用。这个方法将在给定 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状态码。

最后更新于