> For the complete documentation index, see [llms.txt](https://potoyang.gitbook.io/spring-in-action-v4/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-v4/di-16-zhang-shi-yong-spring-mvc-chuang-jian-rest-api/untitled-1/untitled.md).

# 16.4.10　交换资源

到目前为止，我们已经看到 RestTemplate 的各种方法来 GET、PUT、DELETE 以及 POST 资源。在它们之中，我们看到两个特殊的方法：getForEntity() 和 postForEntity()，这两个方法将结果资源包含在一个 ResponseEntity 对象中，通过这个对象我们可以得到响应头和状态码。

能够从响应中读取头信息是很有用的。但是如果你想在发送给服务端的请求中设置头信息的话，怎么办呢？这就是 RestTemplate 的 exchange() 的用武之地。

像 RestTemplate 的其他方法一样，exchange() 也重载为三个签名格式。一个使用 java.net.URI 来标识目标 URL，而其他两个以 String 的形式传入 URL 并带有 URL 变量。如下所示：

```java
<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
			Class<T> responseType) throws RestClientException;

<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
			Class<T> responseType, Object... uriVariables) throws RestClientException;

<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
			Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
```

exchange() 方法使用 HttpMethod 参数来表明要使用的 HTTP 动作。根据这个参数的值，exchange() 能够执行与其他 RestTemplate 方法一样的工作。

例如，从服务器端获取 Spitter 资源的一种方式是使用 RestTemplate 的 getForEntity() 方法，如下所示：

```java
ResponseEntity<Spitter> response = rest.getForEntity(
  "http://localhost:8080/spittr-api/spitters/{spitter}",
  Spitter.class, spitterId);
Spitter spitter = response.getBody();
```

在下面的代码片段中，可以看到 exchange() 也可以完成这项任务：

```java
ResponseEntity<Spitter> response = rest.exchange(
  "http://localhost:8080/spittr-api/spitters/{spitter}",
  HttpMethod.GET, null, Spitter.class, spitterId);
Spitter spitter = response.getBody();
```

通过传入 HttpMethod.GET 作为 HTTP 动作，我们会要求 exchange() 发送一个 GET 请求。第三个参数是用于在请求中发送资源的，但因为这是一个 GET 请求，它可以是 null。下一个参数表明我们希望将响应转换为 Spitter 对象。最后一个参数用于替换 URL 模板中 {spitter} 占位符的值。

按照这种方式，exchange() 与之前使用的 getForEntity() 是几乎相同的，但是，不同于 getForEntity() —— 或 getForObject() —— exchange() 方法允许在请求中设置头信息。接下来，我们不再给 exchange() 传递 null，而是传入带有请求头信息的 HttpEntity。

如果不指明头信息，exchange() 对 Spitter 的 GET 请求会带有如下的头信息：

```http
GET /Spitter/spitters/habuma HTTP/1.1
Accept: application/xml, text/xml, application/*+xml, application/json
Content-Length: 0
User-Agent: Java/1.6.0_20
Host: localhost:8080
Connection: keep-alive
```

让我们看一下 Accept 头信息。Accept 头信息表明它能够接受多种不同的 XML 内容类型以及 application/json。这样服务器端在决定采用哪种格式返回资源时，就有很大的可选空间。假设我们希望服务端以 JSON 格式发送资源。在这种情况下，我们需要将 “application/json” 设置为 Accept 头信息的唯一值。

设置请求头信息是很简单的，只需构造发送给 exchange() 方法的 HttpEntity 对象即可，HttpEntity 中包含承载头信息的 MultiValueMap：

```java
MultiValue<String, String> headers = new LinkedMultiValue<String, String>();
headers.add("Accept", "application/json");
HttpEntity<Object> requestEntity = new HttpEntity<Object>(headers);
```

在这里，我们创建了一个 LinkedMultiValueMap 并添加值为 “application/json” 的 Accept 头信息。接下来，我们构建了一个 HttpEntity（使用 Object 泛型类型），将 MultiValueMap 作为构造参数传入。如果这是一个 PUT 或 POST 请求，我们需要为 HttpEntity 设置在请求体中发送的对象 —— 对于 GET 请求来说，这是没有必要的。

现在，我们可以传入 HttpEntity 来调用 exchange()：

```java
ResponseEntity<Spitter> response = rest.exchange(
  "http://localhost:8080/spittr-api/spitters/{spitter}",
  HttpMethod.GET, requestEntity, Spitter.class, spitterId);
Spitter spitter = response.getBody();
```

表面上看，结果是一样的。我们得到了请求的 Spitter 对象。但在表面之下，请求将会带有如下的头信息发送：

```http
GET /Spitter/spitters/habuma HTTP/1.1
Accept: application/json
Content-Length: 0
User-Agent: Java/1.6.0_20
Host: localhost:8080
Connection: keep-alive
```

假设服务器端能够将 Spitter 序列化为 JSON，响应体将会以 JSON 格式来进行表述。
