為什麼 HTTP GET 請求不應該有主體
一、概述
在本教程中,我們將了解為什麼不應將 HTTP GET 請求與正文一起發送。
首先,我們將了解通常使用該請求的原因。然後,我們將研究發送它的後果。最後,我們將探討在 GET 請求中包含正文的替代方法。
2. 為什麼有些 GET 請求是和 body 一起發送的?
在開發 REST API 時,我們可能會遇到需要將數據附加到 GET 請求的主體。當執行複雜的搜索或者URI 太長服務器無法處理時,就會發生這種情況。它起源於對初始HTTP/1.1規範的解釋。然後,它變得方便和廣泛。
2.1.不明確的 HTTP/1.1 規範
RFC 7231定義了 HTTP 語義和內容。不幸的是,它沒有明確說明帶有正文的 GET 請求應該發生什麼:
GET 請求消息中的負載沒有定義的語義。
這句話有多種解釋。這可能意味著語義留給了規範的每個實現者。但這也可能意味著這樣的請求沒有語義。因此,一些 HTTP 服務器允許它。其他瀏覽器、負載均衡器和緩存代理則沒有。
2.2.方便
為了方便起見,我們可能希望發送帶有正文的 GET 請求。首先,因為編碼 JSON 負載比處理複雜的查詢字符串更容易。 JSON 更加結構化,並且內置支持多種編程語言。這影響了Elasticsearch empty_search API中的一些設計決策:
請求主體搜索允許我們使用
query domain-specific language
或查詢 DSL 來編寫查詢,而不是神秘的查詢字符串方法。
其次,如果我們控制了客戶端和服務器,那麼在請求體中發送數據就更容易了。我們知道它會被服務器接收。
第三,發送帶有正文的 GET 比 POST 更美觀。後者似乎違反直覺。這是因為 POST 請求在很大程度上被視為創建資源的一種方式。 Elasticsearch 的作者圍繞它構建了他們的搜索 API :
Elasticsearch 的作者更喜歡使用 GET 作為搜索請求,因為他們認為它比 POST 動詞更能描述動作—— 檢索信息。但是,由於沒有普遍支持帶有請求主體的 GET,因此搜索 API 也接受 POST 請求:
3. 為什麼用身體執行 GET 是個壞主意?
在大多數情況下,擁有 GET 請求正文可提供更好的可讀性和更好的查詢參數開發人員體驗 (DX) 。雖然技術上可以執行,但出於多種原因,應高度避免此類請求。
3.1. RFC **9110**合規性
2014 年, RFC 7231取代了HTTP/1.1 .
它添加了一個警告,禁止對正文使用 GET:
GET 請求消息中的負載沒有定義的語義;在 GET 請求上發送有效負載主體可能會導致某些現有實現拒絕該請求。
2019 年,其繼任者RFC 9110收緊了 GET 與主體的語言:
客戶端不應該在 GET 請求中生成內容,除非直接向源服務器發出內容,該源服務器先前已在帶內或帶外指示此類請求具有目的並將得到充分支持。源服務器不應該依賴私有協議來接收內容,因為 HTTP 通信的參與者通常不知道請求鏈上的中介
引入該修正案是為了明確清除HTTP/1.1引起的歧義。最好不要在 GET 請求中發送正文。這保證了與所有系統的完全互操作性,無論規範版本如何。
3.2.忽略的請求正文
HTTP/1.1最初建議忽略 GET 請求體:
如果請求方法不包括為實體主體定義的語義,那麼在處理請求時應該忽略消息主體
因此,一些實現它的舊服務器將無法正確處理我們的請求。
3.3.拒絕請求
某些 HTTP 實現拒絕具有請求正文的 GET 請求。事實上, fetch
API 在這種情況下會拋出錯誤。同樣,Spring 在傳遞主體時拋出HttpMessageNotReadableException
。
3.4.未緩存的請求
RFC 9110**指出 GET 請求應該是可緩存的。如果我們發送一個正文,就會違反該約束,因為代理不會讀取該請求正文。由於未緩存數據,我們將因多次服務器調用而遇到性能問題。**
4. GET with a body 的替代方案是什麼?
讓我們探討一些使用 GET 請求主體的替代方法。其中一些還沒有標準化。
4.1. HTTP POST
首先,POST 是 GET 的可行替代品。實際上,在所有 HTTP 方法中,POST 的限制最少。我們總是可以默認它。如果請求的語義符合其約束,我們應該更願意使用更具體的方法。
最後,每當我們想要向 GET 請求添加正文時,使用 POST 總是安全的。
4.2.查詢
接下來,我們有QUERY方法。它應該有助於發出包含內容的安全、可緩存、冪等的請求。具體來說,它接受包含查詢操作的請求負載:
QUERY /contacts HTTP/1.1
Host: example.org
Content-Type: example/query
Accept: text/csv
select surname, givenname, email limit 10
此請求應返回成功響應:
HTTP/1.1 200 OK
Content-Type: text/csv
surname, givenname, email
Smith, John, [email protected]
Jones, Sally, [email protected]
Dubois, Camille, [email protected]
QUERY 方法是一個很有前途的提議。在它成為一個實施的規範之前,它不能被公眾使用。
4.3. HTTP 搜索
最後,我們可以使用非標準的 SEARCH 方法。它類似於 QUERY 但接受 JSON 或 XML 類型的主體:
SEARCH /search HTTP/1.1
Host: www.example.com
Content-Type: application/json
{
"query": "baeldung",
"sort": "date",
"filters": {
"type": "article",
"category": "tech"
}
}
服務器可以理解 SEARCH 主體的每個鍵。所有這些都用於執行搜索並返回適當的內容。**該方法尚未普及,僅適用於WebDAV SEARCH 規範**。
5.結論
在本文中,我們了解了為什麼我們不應該發送帶有正文的 HTTP GET 請求。我們首先看到這些請求源自對初始 HTTP 1.1 規範的解釋。
接下來,我們探討了它們可能造成的許多不良後果。最後,我們討論了各種可用的替代方案。
總的來說,與最新的 HTTP 規範保持一致總是一個好主意。它保證具有可互操作的軟件。