> 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.5-qing-qiu-zhuan-huan.md).

# 11.4.5 请求转换

到目前为止，您已经使用了 WebClient 的 `retrieve()` 方法来发送请求。在这些情况中，`retrieve()` 方法返回 ResponseSpec 类型的对象。通过它可以使用 `onStatus()`、`bodyToFlux()` 和 `bodyToMono()` 等方法处理响应。使用 ResponseSpec 对象适用于简单的一些情况，某些方面会受到限制。举例来说，如果你需要获取响应的 header 或 cookie， 使用 ResponseSpec 就无法完成。

当 ResponseSpec 不满足需求时，可以使用 `exchange()` 取代 `retrieve()`。`exchange()` 方法返回 ClientResponse 类型的 Mono，您可以应用反应式操作来检查和使用完整的返回数据，包括有效载荷、headers 和 Cookies。

在研究 `exchange()` 与 `retrieve()` 的区别之前，让我们先看看他们有多相似。下面的代码片段使用 WebClient 和 `exchange()` ，按 ID 获取单个 Ingredient：

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

这大致相当于下面使用 `retrieve()` 的示例：

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

在 `exchange()` 示例中，没有使用 ResponseSpec 对象的 `bodyToMono()` 获得 `Mono<Ingredient>`。而是通过一个扁平化映射函数，将 ClientResponse 映射到 `Mono<Ingredient>`，从而得到 `Mono<ClientResponse>` ，也即一个 Mono。

现在让我们看看 `exchange()` 有什么不同之处。让我们假设，请求中可能包含一个名为 `X_UNAVAILABLE` 的请求头。当其值为 `true` 时，表示（由于某种原因）相关的 Ingredient 不可用。为了便于讨论，假设该头存在，您肯定希望得到的结果 Mono 为空，不返回任何内容。这可以通过添加另一个对 `flatMap()` 的调用来实现。整个 WebClient 调用像如下这样：

```java
Mono<Ingredient> ingredientMono = webClient
    .get()
    .uri("http://localhost:8080/ingredients/{id}", ingredientId)
    .exchange()
    .flatMap(cr -> {
        if (cr.headers().header("X_UNAVAILABLE").contains("true")) {
        return Mono.empty();
        }
        return Mono.just(cr);
    })
    .flatMap(cr -> cr.bodyToMono(Ingredient.class));
```

新的 `flatMap()` 调用，检查给定 ClientRequest 对象的响应头。查找名称为 `X_UNAVAILABLE` 且值为 `true` 的 header。如果找到，则返回一个空的 Mono。否则，它将返回一个包含 ClientResponse 的新 Mono 对象。无论是哪种情况，Mono 对象都将通过 `flatMap()` 进一步扁平化映射后再返回。
