A Guide to RestClient in Spring Boot – Spring Boot 中的 RestClient 指南

最后修改: 2023年 10月 27日

中文/混合/英文(键盘快捷键:t)

 1. Introduction

1.导言

RestClient is a synchronous HTTP client introduced in Spring Framework 6.1 M2 that supersedes RestTemplate. A synchronous HTTP client sends and receives HTTP requests and responses in a blocking manner, meaning it waits for each request to complete before proceeding to the next one.

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

In this article, we’ll explore what RestClient offers and how it compares to RestTemplate.

在本文中,我们将探讨 RestClient 提供了哪些功能,以及它与 RestTemplate 相比有何不同。

2. RestClient and RestTemplate

2. RestClientRestTemplate</em

RestTemplate, as the name suggests, is built on a template design pattern. It’s a behavioral design pattern that defines the skeleton of an algorithm in a method, allowing subclasses to provide specific implementations for certain steps. While it’s a powerful pattern, it creates a need for overloading, which can be inconvenient.

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

To improve on this, RestClient features a fluent API. A fluent API is a design pattern that allows method chaining in a way that makes the code more readable and expressive by sequentially calling methods on an object, often without the need for intermediate variables.

为了改进这一点,RestClient 采用了流畅的 API。流畅的 API 是一种设计模式,它允许方法链,通过顺序调用对象上的方法,使代码更具可读性和表现力,通常不需要中间变量。

Let’s start with creating a basic RestClient:

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

RestClient restClient = RestClient.create();

3. Simple Fetching with HTTP Request Methods

3.使用 HTTP 请求方法进行简单提取

Similarly to RestTemplate or any other rest client, RestClient allows us to make HTTP calls with request methods. Let’s walk through different HTTP methods to create, retrieve, modify, and delete resources.

RestTemplate 或任何其他休息客户端类似,RestClient 允许我们使用请求方法进行 HTTP 调用。让我们了解一下创建、检索、修改和删除资源的不同 HTTP 方法。

We’ll operate on an elementary Article class:

我们将在一个基本的 Article 类上进行操作:

public class Article {
    Integer id;
    String title;
    // constructor and getters
}

3.1. Use GET to Retrieve Resources

3.1.使用 GET 获取资源

We use the GET HTTP method to request and retrieve data from a specified resource on a web server without modifying it. It’s primarily employed for read-only operations in web applications.

我们使用 GET HTTP 方法从网络服务器上的指定资源请求和检索数据,而不对其进行修改。它主要用于网络应用程序中的只读操作。

For starters, let’s get a simple String as the response without any serialization to our custom class:

首先,让我们获取一个简单的 String 作为响应,而不对我们的自定义类进行任何序列化:

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

3.2. Use POST to Create a Resource

3.2.使用 POST 创建资源

We use the POST HTTP method to submit data to a resource on a web server, often to create new records or resources in web applications. Unlike the GET method, which retrieves data, POST is designed for sending data to be processed by the server, such as when submitting a web form.

我们使用 POST HTTP 方法向网络服务器上的资源提交数据,通常是为了在网络应用程序中创建新记录或资源。与检索数据的 GET 方法不同,POST 是为发送数据供服务器处理而设计的,例如在提交网络表单时。

The URI should define what resource we want to process.

URI 应定义我们要处理的资源。

Let’s send a simple Article with an ID equal to 1 to our server:

让我们向服务器发送一篇 ID 为 1 的简单文章:

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

Because we specified the “APPLICATION_JSON” content type, the instance of the Article class will be automatically serialized to JSON by the Jackson library under the hood. In this example, we ignore the response body using the toBodilessEntity() method. A POST endpoint doesn’t need to, and often doesn’t, return any payload.

由于我们指定了”APPLICATION_JSON”内容类型,Jackson 库会将 Article 类的实例自动序列化为 JSON。在本例中,我们使用 toBodilessEntity() 方法忽略了响应体。POST 端点不需要返回任何有效载荷,通常也不会返回任何有效载荷。

3.3. Use PUT to Update a Resource

3.3.使用 PUT 更新资源

Next, we’ll look at the PUT HTTP method employed to update or replace an existing resource with the data provided. It’s commonly used for modifying existing entities or other resources in web applications. Typically, we need to specify the updated resource, ensuring a complete replacement.

接下来,我们将学习 PUT HTTP 方法,该方法用于使用所提供的数据更新或替换现有资源。它通常用于修改网络应用程序中的现有实体或其他资源。通常,我们需要指定更新的资源,以确保完全替换。

Let’s modify the article we created in the previous paragraph. The URI we provide should identify the resource we want to change:

让我们修改上一段中创建的文章。我们提供的 URI 应标识我们要修改的资源:

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();

Similarly to the previous paragraph, we rely on RestClient to serialize our payload and ignore the response.

与上一段类似,我们依靠 RestClient 序列化我们的有效载荷,并忽略响应。

3.4. Use DELETE to Remove a Resource

3.4.使用 DELETE 删除资源

We use the DELETE HTTP method to request the removal of a specified resource from a web server. Similarly to GET endpoints, we usually don’t provide any payload for the request and rely on parameters encoded in the URI:

我们使用 DELETE HTTP 方法请求从网络服务器中删除指定的资源。与 GET 端点类似,我们通常不为请求提供任何有效负载,而是依赖于 URI 中编码的参数:

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

4. Deserializing Response

4.反序列化响应

We often want to serialize the request and deserialize the response to some class we can efficiently operate on. The RestClient is equipped with the ability to perform JSON-to-object conversions, a functionality powered by the Jackson library.

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

Moreover, we can use all data types supported by RestTemplate because of the shared utilization of message converters.

此外,由于共享使用了消息转换器,我们可以使用 RestTemplate 支持的所有数据类型。

Let’s retrieve an article by its ID and serialize it to the instance of the Article class:

让我们根据 ID 检索一篇文章,并将其序列化为 Article 类的实例:

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

Specifying the class of the body is a bit more complicated when we want to get an instance of some generic class like List. For example, if we want to get all the articles, we’ll get the List<Article> object. In that case, we can use the ParameterizedTypeReference abstract class to tell RestClient what object we’ll get.

如果我们要获取某个通用类(如 List 的实例),指定正文的类就比较复杂。例如,如果我们想获得所有文章,我们将获得 List<Article> 对象。在这种情况下,我们可以使用 ParameterizedTypeReference 抽象类来告诉 RestClient 我们将获得什么对象。

We don’t even need to specify the generic type, Java will infer the type for us:

我们甚至不需要指定泛型类型,Java 会为我们推断出类型:

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

5. Parsing Response with Exchange

5.用 Exchange 解析响应

The RestClient includes the exchange() method for handling more advanced situations by granting access to the underlying HTTP request and response. In that case, the library won’t apply default handlers, and we must process the status ourselves.

RestClient包含exchange()方法,用于通过授予对底层 HTTP 请求和响应的访问权限来处理更高级的情况。在这种情况下,库不会应用默认处理程序,我们必须自己处理状态。

Let’s say the service we’re communicating with returns a 204 status code when no articles are in the database. Because of that slightly nonstandard behavior, we want to handle it in a special way. We’ll throw an ArticleNotFoundException exception when the status code is equal to 204 and also a more generic exception when the status code is not equal to 200:

比方说,当数据库中没有文章时,与我们通信的服务会返回 204 状态代码。由于这种行为略显非标准,我们希望以一种特殊的方式来处理它。当状态代码等于 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();
      }
});

Because we are working with a raw response here, we also need to deserialize the body of the response ourselves using ObjectMapper.

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

6. Error Handling

6. 错误处理

By default, when RestClient encounters a 4xx or 5xx status code in the HTTP response, it raises an exception that is a subclass of RestClientException. We can override this behavior by implementing our status handler.

默认情况下,当 RestClient 在 HTTP 响应中遇到 4xx 或 5xx 状态代码时,它会引发一个属于 RestClientException 子类的异常。我们可以通过实现我们的状态处理程序来覆盖这一行为。

Let’s write one that will throw a custom exception when we can’t find the article:

让我们编写一个程序,在找不到文章时抛出一个自定义异常:

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

7. Building RestClient from RestTemplate

7.从RestTemplate构建RestClient</em

RestClient is the successor of RestTemplate, and in older codebases, we’re very likely to encounter implementation using RestTemplate.

RestClientRestTemplate的继承者,在旧代码库中,我们很可能会遇到使用RestTemplate的实现。

Fortunately, it’s straightforward to create a RestClient instance with a configuration of the old RestTemplate:

幸运的是,使用旧版 RestTemplate 的配置创建 RestClient 实例非常简单:

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

8. Conclusion

8.结论

In this article, we looked into the RestClient class, the successor of RestTemplate as a synchronous HTTP client. We learned how to use its fluent API for simple and complicated use cases. We started rounding up all the HTTP methods and then moved to response serialization and error-handling topics.

在本文中,我们研究了 RestClient 类,它是作为同步 HTTP 客户端的 RestTemplate 的后续类。我们学习了如何在简单和复杂的用例中使用其流畅的 API。我们开始学习所有 HTTP 方法,然后转向响应序列化和错误处理主题。

As usual, all the code examples are available over on GitHub.

与往常一样,所有代码示例均可在 GitHub 上获取