1. 引言
Spring 生態系統中,有一類名為 <em >RestTemplate</em> 的類。<strong >該實用類是一個高級類,用於發送 HTTP 消息並處理響應</strong>。
在本教程中,我們將探討 <em >RestTemplate</em> 類中 <em >exchange()</em >,postForEntity(), 和 <em >execute()</em > 方法之間的差異。
2. 什麼是 <em >RestTemplate</em>?
正如上面提到的,<em >RestTemplate</em> 是 Spring Framework 中的一個實用類,它簡化了發送 HTTP 消息和處理響應的過程。<em >RestTemplate</em> 類非常適合編寫簡單的 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() 方法
發送 POST 請求的首選和最簡單的方法是使用 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);主要的區別在於,我們不再直接將普通的 Java 對象作為請求體傳遞,而是將其包裹在 HttpEntity 中。 這允許我們明確地為請求設置額外的 HTTP 頭部。
另一個顯著的區別是,exchange() 方法是通用的,這意味着它可以用於任何 HTTP 方法。 因此,請求方法的第二個參數必須指示用於請求的方法。
4.3. 使用 execute() 方法
通過使用 `execute()` 方法,我們可以最終發送我們的 POST 請求。此方法是最通用的,實際上它是 所有其他方法內部所使用的底層方法。
以下是使用 `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);同樣,<em>RestTemplate</em> 提供了快速創建 <em>ResponseExtractor</em> 實例的方法工廠。
ResponseExtractor<ResponseEntity<Book>> responseExtractor = restTemplate.responseEntityExtractor(Book.class);當然,同時使用這兩種工廠方法將會抵消使用 execute() 方法的任何好處。如果我們選擇同時使用它們,我們最好直接使用特定動詞的方法或 exchange() 方法。
5. 結論
在本文中,我們探討了使用 <em >RestTemplate</em> 發送 HTTP POST 請求的三種不同方法。首先,我們看到了如何使用基於動詞的 <em >postForEntity()</em> 方法來創建簡潔明瞭的 HTTP 請求。然後,我們研究了兩種替代方法,<em >exchange()</em> 和 <em >execute()</em>,用於發送相同的請求。
儘管這三種方法都產生相同的結果,但它們各自具有優缺點。<em >postForEntity()</em> 方法導致代碼更少,但同時也意味着我們對生成的 HTTP 請求的控制力較弱。<em >exchange()</em> 和 <em >execute()</em> 方法則提供了更多的請求控制,但同時也使我們的代碼更冗長,並且依賴於 Spring 框架的自動功能較少。