> For the complete documentation index, see [llms.txt](https://potoyang.gitbook.io/spring-in-action-v5/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://potoyang.gitbook.io/spring-in-action-v5/di-11-zhang-kai-fa-xiang-ying-shi-api/11.4-xiang-ying-shi-xiao-fei-rest-api/11.4.4-chu-li-qing-qiu-cuo-wu.md).

# 11.4.4 处理请求错误

到目前为止，所有的 WebClient 示例都假设了一个圆满的结局；请求不会返回任何 400 或 500 级别的 HTTP 状态码。当这些错误状态返回时，WebClient 应该会记录失败日志；否则，静悄悄地忽略它。

如果需要处理此类错误，则可以使用对 `onStatus()` 的调用来指定如何处理各种 HTTP 状态码。`onStatus()` 接受两个函数：一个 Predicate 函数，用于匹配 HTTP 状态。另一个函数给定 ClientResponse 对象，返回 `Mono<Throwable>`。

为了演示如何使用 `onStatus()` 创建自定义错误处理程序，考虑以下通过 ID 获取 Ingredient 的用法：

```java
Mono<Ingredient> ingredientMono = webClient
    .get()
    .uri("http://localhost:8080/ingredients/{id}", ingredientId)
    .retrieve()
    .bodyToMono(Ingredient.class);
```

只要 ingredientId 中的值与已知的资源匹配，那么生成的 Mono 将在订阅时发布返回 Ingredient 对象。但是，如果没有匹配的 Ingredient 对象，那会发生什么事情呢？

当订阅 Mono 或 Flux 可能会发生错误时，注册一个出错处理函数是很重要的。就像在调用 `subscribe()` 方法时注册数据处理函数一样：

```java
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()` 的调用：

```java
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()` 的调用更改来实现，就像这样：

```java
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状态码。
