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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET 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?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
