RestTemplate 教程

1、概览

本文将带你了解如何使用 Spring REST 客户端 RestTemplate 发起各种类型的 HTTP 请求。

2、废弃警告

从 Spring Framework 5 开始,除了 WebFlux,Spring 还引入了一个名为 WebClient 的新 HTTP 客户端。 WebClient 是一种替代 RestTemplate 的现代 HTTP 客户端。它不仅提供传统的同步 API,还支持高效的非阻塞和异步方法。

如果要开发新的应用或迁移旧的应用,最好还是使用 WebClient。在未来版本中,RestTemplate 将被弃用。

3、使用 GET 检索资源

3.1、获取纯 JSON 格式

使用 getForEntity() API 发起 GET 请求:

RestTemplate restTemplate = new RestTemplate();
String fooResourceUrl
  = "http://localhost:8080/spring-rest/foos";
ResponseEntity<String> response
  = restTemplate.getForEntity(fooResourceUrl + "/1", String.class);
Assertions.assertEquals(response.getStatusCode(), HttpStatus.OK);

可以访问完整的 HTTP 响应,因此可以检查 HTTP 状态码是否成功,或者处理响应体:

ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(response.getBody());
JsonNode name = root.path("name");
Assertions.assertNotNull(name.asText());

如上,将响应体作为标准字符串(String)返回,并使用 Jackson(以及 Jackson 提供的 JSON 节点结构)来验证一些细节。

3.2、检索 POJO 而非 JSON

还可以将响应直接映射到资源 DTO:

public class Foo implements Serializable {
    private long id;

    private String name;
    // 标准的 get 、set 方法
}

只需要调用 template 的 getForObject API 即可:

Foo foo = restTemplate
  .getForObject(fooResourceUrl + "/1", Foo.class);
Assertions.assertNotNull(foo.getName());
Assertions.assertEquals(foo.getId(), 1L);

4、使用 HEAD 检索 Header

使用 headForHeaders() API:

HttpHeaders httpHeaders = restTemplate.headForHeaders(fooResourceUrl);
Assertions.assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON));

5、使用 POST 创建资源

可以使用 postForLocation()postForObject()postForEntity() 方法 在 API 中创建新资源。

前者(postForLocation)返回新创建资源的 URI,后者返回资源本身。

5.1、postForObject() API

RestTemplate restTemplate = new RestTemplate();

HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
Foo foo = restTemplate.postForObject(fooResourceUrl, request, Foo.class);
Assertions.assertNotNull(foo);
Assertions.assertEquals(foo.getName(), "bar");

5.2、postForLocation() API

不返回完整的资源,只返回新创建资源的位置:

HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
URI location = restTemplate
  .postForLocation(fooResourceUrl, request);
Assertions.assertNotNull(location);

5.3、exchange() API

可以使用更通用的 exchange API 进行 POST 请求:

RestTemplate restTemplate = new RestTemplate();
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
ResponseEntity<Foo> response = restTemplate
  .exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);
 
Assertions.assertEquals(response.getStatusCode(), HttpStatus.CREATED);
 
Foo foo = response.getBody();
 
Assertions.assertNotNull(foo);
Assertions.assertEquals(foo.getName(), "bar");

5.4、提交表单数据

接着来看看如何使用 POST 方法提交表单。

首先,需要将 Content-Type 头设置为 application/x-www-form-urlencoded

这样就可以向服务器发送包含以 & 分隔的 name/value 对的查询字符串:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

可以将表单变量封装到 LinkedMultiValueMap 中:

MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add("id", "1");

接下来,使用 HttpEntity 实例创建请求:

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);

最后,可以调用 restTemplate.postForEntity() 来请求 REST 服务: /foos/form

ResponseEntity<String> response = restTemplate.postForEntity(
  fooResourceUrl+"/form", request , String.class);
Assertions.assertEquals(response.getStatusCode(), HttpStatus.CREATED);

6、使用 OPTIONS 获取允许的操作

使用 optionsForAllow 方法来获取指定 URL 上允许的操作(请求方法):

Set<HttpMethod> optionsForAllow = restTemplate.optionsForAllow(fooResourceUrl);
HttpMethod[] supportedMethods
  = {HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE};
Assertions.assertTrue(optionsForAllow.containsAll(Arrays.asList(supportedMethods)));

7、使用 PUT 请求更新资源

PUT 请求,更具体地说,是该请求的 exchange() API。

7.1、使用 exchange() 发起简单的 PUT 请求

对 API 发起 PUT 请求,注意,该请求没有响应体。

Foo updatedInstance = new Foo("newName");
updatedInstance.setId(createResponse.getBody().getId());
String resourceUrl = 
  fooResourceUrl + '/' + createResponse.getBody().getId();
HttpEntity<Foo> requestUpdate = new HttpEntity<>(updatedInstance, headers);
template.exchange(resourceUrl, HttpMethod.PUT, requestUpdate, Void.class);

7.2、使用 exchange() 以回调方式发起 PUT 请求

如下,在回调中可以设置所需的所有 Header 信息以及请求体:

RequestCallback requestCallback(final Foo updatedInstance) {
    return clientHttpRequest -> {
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(clientHttpRequest.getBody(), updatedInstance);
        clientHttpRequest.getHeaders().add(
          HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        clientHttpRequest.getHeaders().add(
          HttpHeaders.AUTHORIZATION, "Basic " + getBase64EncodedLogPass());
    };
}

接下来,用 POST 请求创建资源:

ResponseEntity<Foo> response = restTemplate
  .exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class);
Assertions.assertEquals(response.getStatusCode(), HttpStatus.CREATED);

然后更新资源:

Foo updatedInstance = new Foo("newName");
updatedInstance.setId(response.getBody().getId());
String resourceUrl =fooResourceUrl + '/' + response.getBody().getId();
restTemplate.execute(
  resourceUrl, 
  HttpMethod.PUT, 
  requestCallback(updatedInstance), 
  clientHttpResponse -> null);

8、使用 DELETE 删除资源

使用 delete() API 删除现有资源:

String entityUrl = fooResourceUrl + "/" + existingResource.getId();
restTemplate.delete(entityUrl);

9、超时配置

只需使用 ClientHttpRequestFactory 就能配置 RestTemplate 超时:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int timeout = 5000;
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory
      = new HttpComponentsClientHttpRequestFactory();
    clientHttpRequestFactory.setConnectTimeout(timeout);
    return clientHttpRequestFactory;
}

还可以通过 HttpClient 进行更多配置:

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int timeout = 5000;
    RequestConfig config = RequestConfig.custom()
      .setConnectTimeout(timeout)
      .setConnectionRequestTimeout(timeout)
      .setSocketTimeout(timeout)
      .build();
    CloseableHttpClient client = HttpClientBuilder
      .create()
      .setDefaultRequestConfig(config)
      .build();
    return new HttpComponentsClientHttpRequestFactory(client);
}

10、总结

本文介绍了如何使用 RestTemplate 发起各种方式的请求。


Ref:https://www.baeldung.com/rest-template