1. 簡介
在 Spring 生態系統中,有一類名為 RestTemplate 的類。 該實用類是用於發送 HTTP 消息和處理響應的高級類。
在本教程中,我們將探討 exchange()、postForEntity() 和 execute() 方法之間的差異,這些方法是 RestTemplate 類中的。
2. 何為 RestTemplate?
如上所述,RestTemplate 是 Spring Framework 中的一個實用類,它簡化了發送 HTTP 消息和處理響應的過程。 RestTemplate 類非常適合編寫簡單的 HTTP 客户端,因為它提供了許多功能:
- 支持所有標準 HTTP 動詞 (GET, POST 等)
- 能夠處理所有標準 MIME 類型 (JSON, XML, 形式編碼等)
- 高層 API 允許我們與 Java 類一起工作,並避免複雜的序列化問題
- 可以使用 ClientHttpRequestInitializer 和 ClientHttpRequestInterceptor 接口進行自定義
2.1. 棄用警告
從 Spring Framework 5 開始,RestTemplate 類正在逐漸被棄用。 雖然它仍然存在於 Spring Framework 6 中,但維護者明確表示此類的未來增強不會被接受。
由於僅接受 minor bug 和安全修復,建議開發者使用 WebClient 代替。 此類具有更現代的 API,並支持同步、異步和流式使用案例。
3. 基本 RestTemplate 使用
RestTemplate 使得使用標準 HTTP 動詞 通過提供具有相應名稱的公共方法變得容易。
例如,要發送 GET 請求,我們可以使用具有 getFor 前綴的許多重載方法。 還有其他 HTTP 動詞(包括 POST、PUT、DELETE、HEAD 和 PATCH)的類似公共方法。
所有這些方法的結構幾乎相同。 它們基本上只需要有關要發送的 URL 以及請求和響應正文的表示形式的信息。 頭部信息會自動為我們創建。
雖然這些高級方法使編寫 HTTP 客户端變得非常容易,但現實世界並不總是符合 HTTP 規範。 在某些情況下,我們可能需要構建一個不完全符合任何特定動詞方法的 HTTP 請求。
這就是為什麼 RestTemplate 提供了具有粒度方法的通用方法,我們將會在下一部分中探討這些方法。
4. 使用 exchange()、postForEntity() 和 execute() 方法
雖然使用頂層動詞特定方法對於許多用例來説是可以的,但我們很可能會需要對 RestTemplate 生成的 HTTP 請求進行更多控制。 這就是 exchange() 和 execute() 方法發揮作用的地方。
讓我們考慮一個 HTTP POST 請求,它允許我們創建一個新的圖書數據庫條目。 以下是一個封裝請求主體所需的所有數據的 Java 類:
class Book {
String title;
String author;
int yearPublished;
}
下面,我們將使用三個 RestTemplate 方法變體之一來發送此請求。
4.1. 使用 postForEntity() 方法
第一個也是最簡單的方法是使用 postForEntity()。 此方法僅需要 URL 和請求主體,並解析響應主體為 ResponseEntity 對象:
Book book = new Book(
"Cruising Along with Java",
"Venkat Subramaniam",
2023);
ResponseEntity<Book> response = restTemplate.postForEntity(
"https://api.bookstore.com",
book,
Book.class);
在此示例中,我們創建了一個新的書對象,將其發送到服務器,並將響應解析回另一個書對象。 值得注意的是,我們只需要提供遠程 URL、請求對象和用於響應的類。 其他一切,包括 HTTP 標頭,都是 RestTemplate 自動構建的。
4.2. 使用 exchange() 方法
我們可以通過使用 exchange() 方法來發送請求的下一種方式:
Book book = new Book(
"Effective Java",
"Joshua Bloch",
2001);
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("username", "password");
ResponseEntity<Book> response = restTemplate.exchange(
"https://api.bookstore.com",
HttpMethod.POST,
new HttpEntity<>(book, headers),
Book.class);
主要區別在於,我們用 HttpEntity 包裝請求主體,而不是傳遞一個簡單的 Java 對象。 這允許我們使用請求中設置額外的 HTTP 標頭。
另一個明顯的不同之處在於,exchange() 方法是通用的,這意味着它可用於任何 HTTP 方法。 因此,請求方法的第二個參數必須指示用於請求的方法。
4.3. 使用 execute() 方法
發送 POST 請求的最後一種方式是使用 execute() 方法。 此方法是最通用的,而且實際上是所有其他方法內部使用的底層方法。
以下是使用 execute() 方法發送 POST 請求的高級示例:
ResponseEntity<Book> response = restTemplate.execute(
"https://api.bookstore.com",
HttpMethod.POST,
new RequestCallback() {
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
// manipulate request headers and body
}
},
new ResponseExtractor<ResponseEntity<Book>>() {
@Override
public ResponseEntity<Book> extractData(ClientHttpResponse response) throws IOException {
// manipulate response and return ResponseEntity
}
}
);
值得注意的是,雖然我們仍然需要提供 URL 和 HTTP 方法,但其他一切看起來大不相同。 這是因為 execute() 方法不直接處理請求和響應。
它允許我們使用 RequestCallback 和 ResponseExtractor 接口創建和修改它們。 最大的好處是 這使我們能夠對請求和響應對象具有最大的控制權。 然而,我們的代碼不太簡潔,並且我們丟失了其他 RestTemplate 方法提供的許多自動功能。
值得注意的是,RestTemplate 確實提供了一個用於輕鬆創建 RequestCallback 實例的工廠方法。 該方法是 httpEntityCallback(),它具有兩個重載形式,可以幫助我們減少使用 execute() 方法時編寫代碼量:
Book book = new Book(
"Reactive Spring",
"Josh Long",
2020);
RequestCallback requestCallback1 = restTemplate.httpEntityCallback(book);
RequestCallback requestCallback2 = restTemplate.httpEntityCallback(book, Book.class);
同樣,RestTemplate 提供了用於快速創建 ResponseExtractor 實例的工廠方法:
ResponseExtractor<ResponseEntity<Book>> responseExtractor = restTemplate.responseEntityExtractor(Book.class);
當然,使用這兩個工廠方法會使我們失去使用 execute() 方法的任何好處。 如果我們選擇同時使用它們,我們最好只使用動詞特定方法或 exchange() 方法。
5. 結論
在本文中,我們探討了使用 RestTemplate 發送 HTTP POST 請求的三種不同方法。首先,我們看到了如何使用特定動詞的 postForEntity() 方法來創建小而簡潔的 HTTP 請求。然後,我們研究了兩種替代方法,exchange() 和 execute(),用於發送相同的請求。
儘管這三種方法都產生相同的結果,但它們各自具有優缺點。 postForEntity() 方法產生了更少的代碼,但這意味着我們對生成的 HTTP 請求的控制力較弱。 exchange() 和 execute() 方法則提供了我們對請求的更多控制權,但代價是我們的代碼更冗長,並且不太依賴於 Spring 框架的自動功能。