Spring Boot 中 RestClient 指南

REST,Spring Boot
Remote
1
02:13 AM · Dec 01 ,2025

1. 簡介

RestClient 是 Spring Framework 6.1 M2 中引入的同步 HTTP 客户端,取代了 RestTemplate。同步 HTTP 客户端以阻塞方式發送和接收 HTTP 請求和響應,這意味着它在每個請求完成之前不會繼續到下一個請求。

在本教程中,我們將探索 RestClient 的優勢,以及它與 RestTemplate 的比較。

2. RestClientRestTemplate

RestTemplate,正如其名稱所示,基於模板設計模式構建。 這是一個行為設計模式,它在方法中定義算法的骨架,允許子類為某些步驟提供特定實現。 儘管它是一個強大的模式,但它會產生需要重載的需求,這可能會不便。

為了改進這一點,RestClient 具有流式 API。 流式 API 是一種設計模式,它允許通過在對象上順序調用方法來實現方法鏈,通常無需中間變量。

讓我們從創建基本的 RestClient 開始:

RestClient restClient = RestClient.create();

3. 簡單的 HTTP 請求方法 Fetching

類似於 RestTemplate 或任何其他 rest 客户端,RestClient 允許我們使用請求方法進行 HTTP 調用。讓我們逐步瞭解不同的 HTTP 方法,用於創建、檢索、修改和刪除資源。

我們將操作一個 elementary 的 Article 類:

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

3.1. 使用 GET 檢索資源

我們將使用 GET HTTP 方法來請求和檢索指定 Web 服務器上的數據,而無需修改它。 它主要用於 Web 應用程序的只讀操作。

為了開始,讓我們獲取一個簡單的 String 作為響應,而無需進行任何序列化到我們的自定義類:

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

3.2. 使用 POST 創建資源

我們將使用 POST HTTP 方法向 Web 服務器上的資源提交數據,通常用於在 Web 應用程序中創建新記錄或資源。 與 GET 方法不同,它檢索數據,POST 用於將數據發送到服務器進行處理,例如提交 Web 表單。

URI 應該定義我們想要處理的資源。

讓我們將一個 ID 等於 1 的簡單 Article 發送到我們的服務器:

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 內容類型,因此 Article 類的實例將在底層由 Jackson 庫自動序列化為 JSON。在此示例中,我們使用 toBodilessEntity() 方法忽略響應主體。POST 端點通常不需要,並且通常不返回任何內容。

3.3. 使用 PUT 更新資源

接下來,我們將研究 PUT HTTP 方法,該方法用於使用提供的數據更新或替換現有資源。 它通常用於修改現有實體或其他 Web 應用程序中的資源。通常,我們需要指定要更新的資源,確保完全替換。

讓我們修改我們在上一段中創建的文章。我們提供的 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();

類似於上一段,我們將依賴 RestClient 來序列化我們的內容並忽略響應。

3.4. 使用 DELETE 刪除資源

我們將使用 DELETE HTTP 方法來請求從 Web 服務器刪除指定資源。 類似於 GET 端點,我們通常不為請求提供任何內容,而是依賴 URI 中編碼的參數:

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

4. 支持請求屬性

關於支持像 WebClient 那樣的請求屬性類型,重要的是要理解 WebClient 引入了屬性以解決一個限制,具體來説,針對反應式環境的限制。 這種限制是缺乏可靠的線程本地。 由於 RestTemplateRestClient 可以使用線程本地,因此我們很少需要請求屬性。

使用 RestClient 的屬性的一些用例,例如將屬性傳遞給攔截器,確實存在。 為了促進這一點,Spring Framework 6.2 添加了 getAttributesorg.springframework.http.HttpRequest 接口。 它返回了用於 HttpRequest 請求的可變屬性映射。 我們可以創建一個攔截器來更新請求屬性:

@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://example.com/api")
    .build();

String pathVariable = "pathVariable";
ResponseEntity response = restClient.get()
    .uri(uriBuilder -> uriBuilder
      .path("/" + pathVariable)
      .queryParam("param1", "value1")
      .queryParam("param2", "value2")
      .build())
    .header("Content-Type", "application/json")
    .retrieve()
    .toEntity(String.class)
    .block();

5. 反序列化響應

我們經常希望序列化請求並反序列化響應到我們能夠高效操作的類。 RestClient 具備執行 JSON 到對象轉換的功能,該功能由 Jackson 庫提供支持。

此外,由於共享消息轉換器的使用,我們可以使用 RestTemplate 的所有數據類型。

讓我們通過其 ID 檢索一篇文章,並將其序列化到 Article 類實例:

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

當我們需要獲取一些泛型類的實例,例如 List 時,指定 body 的類會變得更加複雜。 例如,如果我們想獲取所有文章,我們將會得到 List<Article> 對象。 在這種情況下,我們可以使用抽象類 ParameterizedTypeReference 來告訴 RestClient 我們將獲得什麼對象。

我們甚至不需要指定泛型類型,Java 會自動為我們推斷類型:

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

6. 解析響應與 Exchange

RestClient 包含 exchange() 方法,用於處理更高級的情況,通過提供對底層 HTTP 請求和響應的訪問權限。因此,該庫不會應用默認的處理程序,並且我們必須自己處理狀態碼。

假設我們與通信的服務在數據庫中沒有文章時返回 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();
      }
});

由於我們正在處理原始響應,因此我們還需要自己使用 ObjectMapper 解析響應體。

7. 錯誤處理

默認情況下,當 RestClient 遇到 HTTP 響應中的 4xx 或 5xx 狀態碼時,它會引發一個繼承自 RestClientException 的異常。 我們可以通過實現自定義狀態處理器來覆蓋此行為。

下面是如何編寫一個在無法找到文章時拋出自定義異常的處理程序:

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

8. 從 RestClient 構建使用 RestTemplate 的客户端

RestClientRestTemplate 的後繼者,在較舊的代碼庫中,我們很可能遇到使用 RestTemplate 的實現。

幸運的是,使用舊版本的 RestTemplate 的配置創建 RestClient 實例非常簡單。

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

9. 結論

在本文中,我們重點介紹了 RestClient 類,它是 RestTemplate 的繼任者,作為同步 HTTP 客户端。我們學習瞭如何使用其流暢 API,用於簡單和複雜的使用案例。接下來,我們開始收集所有 HTTP 方法,然後轉向響應序列化和錯誤處理主題。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.