1. 简介

RestClient 是 Spring Framework 6.1 M2 引入的同步 HTTP 客户端,用于取代 RestTemplate。同步 HTTP 客户端采用阻塞方式发送和接收 HTTP 请求与响应,即必须等待当前请求完成才能处理下一个请求。

本文将深入探讨 RestClient 的核心特性,并与 RestTemplate 进行对比分析。

2. RestClient 与 RestTemplate 对比

RestTemplate 采用模板方法设计模式,这种模式虽然强大,但会导致大量方法重载,使用起来不够灵活。

RestClient 通过流畅式 API (Fluent API) 解决了这个问题。流畅式 API 支持方法链式调用,使代码更简洁易读,且无需中间变量。

创建 RestClient 实例非常简单:

RestClient restClient = RestClient.create();

3. HTTP 请求方法基础操作

与 RestTemplate 类似,RestClient 支持所有标准 HTTP 方法进行资源操作。我们将通过一个简单的 Article 类演示各种操作:

public class Article {
    Integer id;
    String title;
    // 构造方法和 getter
}

3.1. GET 方法获取资源

GET 方法用于从服务器获取数据而不修改资源,是只读操作的首选。

先看一个直接获取字符串响应的简单示例:

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

3.2. POST 方法创建资源

POST 方法用于向服务器提交数据创建新资源,常用于表单提交等场景。

URI 需明确指定要处理的目标资源。下面创建一个 ID 为 1 的文章:

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

由于设置了 APPLICATION_JSON 内容类型,Article 对象会自动通过 Jackson 序列化为 JSON。使用 toBodilessEntity() 忽略响应体,因为 POST 接口通常不返回有效负载。

3.3. PUT 方法更新资源

PUT 方法用于完整替换现有资源,适用于更新操作。

更新之前创建的文章(注意 URI 需包含资源 ID):

Article article = new Article(1, "如何更高效地使用 RestClient");
ResponseEntity<Void> response = restClient.put()
  .uri(uriBase + "/articles/1")
  .contentType(APPLICATION_JSON)
  .body(article)
  .retrieve()
  .toBodilessEntity();

同样依赖 RestClient 自动序列化请求体并忽略响应。

3.4. DELETE 方法删除资源

DELETE 方法用于删除指定资源,通常通过 URI 参数标识目标资源:

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

4. 请求属性支持

RestClient 的请求属性机制与 WebClient 不同:WebClient 引入属性是为了解决响应式环境中线程局部变量不可靠的问题。而 RestClient 和 RestTemplate 可以依赖线程局部变量,因此很少需要请求属性。

不过在某些场景下仍需使用,例如向拦截器传递属性。Spring Framework 6.2 在 HttpRequest 接口中添加了 getAttributes() 方法:

@Test
void updateRequestAttribute() throws Exception {
    String attrName = "attr1";
    String attrValue = "value1";

    assertDoesNotThrow(() -> { 
      ClientHttpRequestInterceptor interceptor = (request, body, execution) -> {
        request.getAttributes().put(attrName, attrValue);
        return execution.execute(request, body);
      };
    });
}

大多数情况下,我们更推荐通过 URI 构建器处理查询参数,而非使用请求属性 API:

RestClient restClient = RestClient.builder()
    .baseUrl("https://api.example.com")
    .build();

String pathVariable = "articles";
ResponseEntity response = restClient.get()
    .uri(uriBuilder -> uriBuilder
      .path("/" + pathVariable)
      .queryParam("page", "1")
      .queryParam("size", "10")
      .build())
    .header("Content-Type", "application/json")
    .retrieve()
    .toEntity(String.class);

5. 响应反序列化

RestClient 内置 JSON 与对象转换能力,底层由 Jackson 库驱动。同时支持 RestTemplate 的所有数据类型,因为它们共享消息转换器。

获取文章并反序列化为 Article 对象:

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

处理泛型类型(如 List<Article>)时,需使用 ParameterizedTypeReference

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

6. 使用 Exchange 解析响应

exchange() 方法提供对底层 HTTP 请求/响应的完全控制,适用于复杂场景。此时不会应用默认处理器,需手动处理状态码。

假设服务在数据库无文章时返回 204 状态码,我们需要特殊处理:

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 手动反序列化响应体。

7. 错误处理

默认情况下,遇到 4xx/5xx 状态码时 RestClient 会抛出 RestClientException 子类异常。可通过自定义状态处理器覆盖此行为。

自定义 404 错误处理:

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

8. 从 RestTemplate 构建 RestClient

作为 RestTemplate 的继任者,RestClient 支持从现有 RestTemplate 实例创建,便于旧项目迁移:

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

9. 总结

本文系统介绍了 Spring Boot 中 RestClient 的核心功能,作为 RestTemplate 的替代方案,它提供了更现代的流畅式 API。我们覆盖了:

  • ✅ 基础 HTTP 方法操作
  • ✅ 请求属性与 URI 构建
  • ✅ 响应反序列化机制
  • ✅ 高级响应处理(exchange)
  • ✅ 自定义错误处理
  • ✅ 从 RestTemplate 迁移方案

对于需要同步 HTTP 客户端的新项目,RestClient 是当前最佳选择。


原始标题:A Guide to RestClient in Spring Boot | Baeldung