處理帶有主體的 Feign GET 請求
1. 引言
HTTP 請求通常是 Web 和 API 通訊的核心。它們是協定和正確資料交換的基石。
在本教學中,我們將探討如何使用 Spring Cloud OpenFeign 處理帶有請求體的 GET 請求。
2. 理解問題
根據 HTTP/1.1 規範,GET 請求不應該包含請求體:
- GET 旨在檢索資料(不發送複雜的有效負載) 。
- 大多數伺服器和代理程式會忽略或拒絕 GET 主體。
- 當 GET 請求有主體時,CDN 或瀏覽器等快取層可能會出現異常。
然而,一些外部 API 或遺留系統仍然需要帶有 body 的 GET 請求來傳遞複雜的搜尋條件。問題在於,Spring Cloud OpenFeign 預設不會序列化 GET 請求的 body——它嚴格遵循 HTTP 標準。不過,只需進行一些調整,就可以處理這種情況。
3. 項目設定
要使用 Feign,我們需要將 OpenFeign 依賴項新增至pom.xml檔:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
接下來,我們需要使用@EnableFeignClients註解在主應用程式類別中啟用它:
@SpringBootApplication
@EnableFeignClients
public class FeignDemoApplication {
public static void main(String[] args) {
SpringApplication.run(FeignDemoApplication.class, args);
}
}
這樣,我們就可以使用對應的功能。
4. 嘗試發送帶有 Body 的 GET 請求
讓我們先看看如果我們天真地嘗試在 Feign 中使用@RequestBody發送 GET 請求會發生什麼。
作為範例,我們首先定義一個Feign客戶端:
@FeignClient(name = "sampleClient", url = "http://localhost:8080")
public interface GetBodyFeignClient {
@GetMapping("/api/search")
String search(@RequestBody SearchRequest searchRequest);
}
接下來我們來定義請求模型:
public class SearchRequest {
private String keyword;
private String category;
// getters and setters
}
如果我們嘗試在測試中呼叫此方法,我們可以看到結果:
SearchRequest request = new SearchRequest();
request.setKeyword("spring");
request.setCategory("tutorial");
Assertions.assertThrows(FeignException.MethodNotAllowed.class, () -> {
getBodyFeignClient.search(request);
});
我們得到一個異常:
feign.FeignException$MethodNotAllowed: status 405 reading SampleFeignClient#search(SearchRequest)
發生這種情況的原因是,GET 方法中請求主體的存在導致底層 Feign 基礎架構建構伺服器未預期的資料物件。
預設情況下,Feign 和 Spring MVC 不允許在 GET 請求中包含 body 訊息,任何嘗試發送 body 訊息的請求都會在發送前被忽略。有些伺服器會直接拒絕此類請求,並回傳 HTTP 400 或 405 錯誤。
5.使用@SpringQueryMap修復GET請求
為了解決這個問題,正確的方法是使用@SpringQueryMap而不是@RequestBody 。此註解告訴 Feign 將資料物件的欄位序列化為 URL 查詢參數,以確保符合 HTTP GET 語義並實現跨伺服器和代理程式的可靠操作。
因此,我們可以更新 Feign 客戶端介面:
@FeignClient(name = "sampleClient", url = "http://localhost:8080")
public interface GetBodyFeignClient {
@GetMapping("/api/search")
String searchWithSpringQueryMap(@SpringQueryMap SearchRequest searchRequest);
}
接下來,我們呼叫相關的設定器並執行請求:
SearchRequest request = new SearchRequest();
request.setKeyword("spring");
request.setCategory("tutorial");
getBodyFeignClient.searchWithSpringQueryMap(request);
Feign 產生如下 GET 請求:
GET http://localhost:8080/api/search?keyword=spring&category=tutorial
值得注意的是, SearchRequest物件中的欄位會自動轉換為查詢參數,無需新增請求體。這種方法避免了我們在嘗試使用 GET 請求傳送請求體時可能遇到的405 Method Not Allowed或400 Bad Request錯誤。重要的是,複雜的資料對象可能並不總是能以這種方式輕鬆轉換,因此需要由開發人員自行處理具體情況。
這種整合方法不僅解決了問題,而且還確保 API 呼叫與標準 HTTP 快取、代理行為和伺服器期望相容,使其更安全、更可預測。
6. 結論
在本文中,我們學習如何使用@SpringQueryMap處理需要多個參數或複雜搜尋條件的 GET 請求,同時完全符合 HTTP 規範。
與往常一樣,原始碼可在 GitHub 上取得。