Spring RestClient 教程

1、简介

RestClientSpring 6.1 M2 中引入的同步 HTTP 客户端,它取代了 RestTemplate。同步 HTTP 客户端以阻塞方式发送和接收 HTTP 请求和响应,这意味着它会等待每个请求完成后才继续下一个请求。

本文将带你了解 RestClient 的功能以及它与 RestTemplate 的比较。

2、RestClientRestTemplate

RestTemplate,顾名思义,是基于模板设计模式构建的。它是一种行为设计模式,用于在方法中定义算法的框架,允许子类为某些步骤提供特定的实现。虽然这是一种强大的模式,但它会导致需要进行方法覆写,这可能是不方便的。

为了改进这一点,RestClient 采用了 Fluent API。Fluent API 也是一种设计模式,它允许以一种使代码更易读和表达的方式进行方法链式调用,通常无需使用中间变量。

从创建一个基本的 RestClient 开始:

RestClient restClient = RestClient.create();

3、使用各种 HTTP 方法发起请求

RestTemplate 或其他 REST 客户端类似,RestClient 可以使用不同的 HTTP 方法发起请求。

创建一个用于操作的 Article 类:

public class Article {
    Integer id;
    String title;
    // 构造函数和 getter/setter
}

3.1、使用 GET 请求获取资源

GET 请求用于检索 Web 服务器上指定资源的数据,而不对其进行修改。通常,这是一个只读的操作!

获取服务器响应的字符串(String):

String result = restClient.get()
  .uri(uriBase + "/articles")
  .retrieve()
  .body(String.class);

3.2、使用 POST 请求创建资源

POST 请求用于提交数据到 Web 服务器上,进行新的资源创建。资源通常是通过 URI 定义的。

向服务器发送一篇 ID 为 1Article

Article article = new Article(1, "How to use RestClient");
ResponseEntity<Void> response = restClient.post()
  .uri(uriBase + "/articles")
  .contentType(APPLICATION_JSON)
  .body(article)
  .retrieve()
  .toBodilessEntity();

由于指定了 APPLICATION_JSON Content Type,Jackson 库会自动将 Article 类实例序列化为 JSON。在本例中,使用 toBodilessEntity() 方法忽略了响应体。POST 端点不需要返回任何 Payload,通常也不会返回任何 Payload。

3.3、使用 PUT 请求更新资源

接下来是 PUT 请求,用于更新或替换已经存在的资源。

修改上一段中创建的 Article,URI 中的 ID 即表示要修改的资源:

Article article = new Article(1, "How to use RestClient even better");
ResponseEntity<Void> response = restClient.put()
  .uri(uriBase + "/articles/1")
  .contentType(APPLICATION_JSON)
  .body(article)
  .retrieve()
  .toBodilessEntity();

与 POST 请求一样,依靠 RestClient 对 Payload 进行序列化,并忽略响应。

3.4、使用 DELETE 请求删除资源

使用 DELETE 请求从 Web 服务器上删除指定资源。与 GET 类似,通常不提供任何 Payload,而是在 URI 参数中指定要删除的资源:

ResponseEntity<Void> response = restClient.delete()
  .uri(uriBase + "/articles/1")
  .retrieve()
  .toBodilessEntity();

4、反序列化响应

通常,我们希望将请求序列化,并将响应反序列化为我们可以操作的类。RestClient 具有执行 JSON 到对象转换的功能,该功能由 Jackson 库提供。

此外,由于共享使用了 Message Converter,因此可以使用 RestTemplate 支持的所有数据类型。

通过 ID 获取一篇文章,并将其序列化为 Article 类的实例:

Article article = restClient.get()
  .uri(uriBase + "/articles/1")
  .retrieve()
  .body(Article.class);

当我们需要获取 List 等泛型类的实例时,指定 body 的类就比较复杂了。例如,如果我们想获取所有 Article,以 List<Article> 对象形式返回。在这种情况下,可以使用 ParameterizedTypeReference 抽象类来告诉 RestClient 我们要获取什么对象。

甚至不需要指定泛型类型,Java 会自动推断出类型:

List<Article> articles = restClient.get()
  .uri(uriBase + "/articles")
  .retrieve()
  .body(new ParameterizedTypeReference<>() {});

5、使用 Exchange 解析响应

RestClient 包含 exchange() 方法。通过提供对底层 HTTP 请求和响应的访问权限,用于处理更复杂的情况。在这种情况下,库不会应用默认的 Handler,我们必须自己处理响应的 Status。

比方说,当数据库中没有文章时,服务会返回 204 (No Content)状态代码。由于这种行为略显非标准,我们希望以一种特殊的方式来处理它。当状态代码等于 204 时,抛出一个 ArticleNotFoundException 异常;当状态代码不等于 200 时,抛出一个更通用的异常:

List<Article> article = restClient.get()
  .uri(uriBase + "/articles")
  .exchange((request, response) -> {
      if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(204))) {
          throw new ArticleNotFoundException();
      } else if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) {
          return objectMapper.readValue(response.getBody(), new TypeReference<>() {});
      } else {
          throw new InvalidArticleResponseException();
      }
});

由于我们在这里处理的是原始响应,因此还需要使用 ObjectMapper 对 Response Body 进行反序列化。

6、异常处理

默认情况下,当 RestClient 在 HTTP 响应中遇到 4xx5xx 状态码时,它会抛出一个 RestClientException 子类的异常。我们可以通过自定义 Status Handler 来覆盖这一行为。

自定义 Status Handler,在找不到文章时抛出一个自定义异常:

Article article = restClient.get()
  .uri(uriBase + "/articles/1234")
  .retrieve()
  .onStatus(status -> status.value() == 404, (request, response) -> {
      throw new ArticleNotFoundException(response)
  })
  .body(Article.class);

7、从 RestTemplate 构建 RestClient

如果你已经在项目中使用上了 RestTemplate,也可以可从 RestTemplate 构建 RestClient

RestTemplate oldRestTemplate;
RestClient restClient = RestClient.create(oldRestTemplate);

8、总结

本文介绍了 RestClient 的特点、用法、反序列化和异常处理。这是一个新的的 Rest 客户端,设计更加优雅,旨在代替掉 RestTemplate


参考:https://www.baeldung.com/spring-boot-restclient