【轉】關於在GET請求中使用body

參考MDN中對安全(Safe)的定義,“任何應用都不應讓 GET 請求修改服務端的狀態”。如果不需要修改狀態,爲什麼要在GET請求裏附帶額外的body呢?用query就足夠了呀。

故事還得從一個bug說起。今天有人問我,爲什麼發到後端的請求400了,我說肯定是參數不對,你去檢查檢查GET、POST之類的方法寫沒寫對,要麼就是字段沒對上,無非是這幾個問題。然後他說檢查過了,沒問題啊;我不太相信,但是看了看前端發送的請求,好像確實沒啥問題:

我說既然這樣,那肯定是後端寫錯了,但後端說他已經用postman測過了,肯定沒問題。這就很有意思了。於是我要來了後端的代碼:
不出所料,後端把GET請求裏的參數當成body的內容了,把@RequestBody改成@RequestParam應該就沒問題了;改完之後果然好了。

這本來是一個很簡單的問題,沒什麼可說的,但是他們接着問我,爲什麼GET請求裏不能用body?我尋思着平時也沒什麼人在GET請求里加body吧,而且一直以來都聽說這麼用不好。但是爲什麼不好呢?所以我就查了一些資料,最後乾脆又去RFC翻了翻。看到官方是這麼說的:

[RFC7231] A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

意思是你往GET請求里加body是不符合規範的,不保證所有的實現都支持(主要是以前的實現,因爲以前曾經有相應的規定),要是出了啥問題別怪我沒提醒你。而且據說老版本的postman是不支持在GET請求里加body的,也是最近才加上的支持;所以要放在以前也就沒這些問題了,以前的postman根本發不了帶body的GET請求。

但是這一條並不是強制規定。我看很多人都強調一點,GET請求不應攜帶請求體,服務器應忽略(或者說丟棄)GET請求的請求體。這一條的確是有依據的,來源如下:

[RFC2616] A server SHOULD read and forward a message-body on any request; if the request method does not include defined semantics for an entity-body, then the message-body SHOULD be ignored when handling the request.

當然,官方也只是說SHOULD,沒有像前文一樣措辭嚴厲地強調類似HEAD這種的請求MUST NOT have a message body:

[RFC2616] A message-body MUST NOT be included in a request if the specification of the request method does not allow sending an entity-body in requests.

但是很可惜的是,RFC2616已經過時了。現在的說法變成了這樣,連SHOULD都直接去掉了,要求更加寬鬆:

[RFC7230] Request message framing is independent of method semantics, even if the method does not define any use for a message body.

難道是因爲錯的人多了,錯的也變成了對的?不過即使是這樣,也並不是在GET請求里加上body的理由。

這個問題算是解決了。但是我看到網上各路大神說到GET加body的時候,還提到一個,就是往GET里加body會導致緩存機制失效。“GET 被設計來用 URI 來識別資源,如果讓它的請求體中攜帶數據,那麼通常的緩存服務便失效了,URI 不能作爲緩存的 Key。”我一開始以爲是在服務器上配置的緩存,比如Nginx的cache之類的機制,後來發現好像並非如此。這個緩存,大概是指那種預加載和後存儲,會涉及到一個網絡請求的“安全性”。如果不安全,顯然是不能緩存的。

那麼,GET和POST的區別和應用?這問題挺複雜。簡而言之,就是“安全”和“不安全”的區別。什麼是安全?不用承擔責任。什麼是不安全?可能需要承擔責任。舉個例子,點擊某個鏈接以同意某個協議,這個請求明顯就是不安全的,因爲需要承擔責任。如果採用GET,就違反了GET應該用於安全請求的規範。因爲瀏覽器可能在你不知情的情況下預加載這個頁面(因爲是“安全”的GET請求),這樣相當於你在不知情的情況下同意了某個協議,這顯然是我們不希望看到的。在契約式的設計裏,違反契約的行爲是會帶來嚴重的後果的。瀏覽器按照契約預加載了安全的GET請求,但這本身是不安全的,帶來的後果自然要由打破契約的人承擔(將這個請求設計成GET的人出來捱打)。

之所以強調“安全”,而不是按照常規的說法強調副作用,因爲有副作用的請求不代表不安全;舉例來說,服務器有一個顯示訪問人數的功能,這個功能就可以用GET來做。雖然每次訪問都會發送改變服務器狀態(計數器)的請求,但用戶不會因爲這個請求承擔責任,這個請求是安全的。至於什麼GET請求的URL有長度限制(後來事實證明其實沒有),什麼GET請求的URL裏不能有中文(或者說非ASCII吧),都只是實現上的區別;從最初的設計上來說區別並不在這裏。

當然,這些都是純粹的理論層面的東西。如果遵守RESTful的規範,採用語義化的GET/POST請求,自然也就不會有這些問題了。因爲通常來說,查詢是安全的;這也是GET的主要作用。

說起來也挺有意思的,學習了這麼久,經常提起RFC,也沒搞清楚RFC究竟是個啥玩意,這次就一併查了。雖然我總覺得這是受到6f名詞解釋的影響……原來是叫“Request For Comments”。

參考文章(不分先後):

1. 不再以訛傳訛,GET和POST的真正區別
2. HTTP GET with request body
3. 誰說 HTTP GET 就不能通過 Body 來發送數據呢?
4. URIs, Addressability, and the use of HTTP GET and POST
5. Safe(安全)
此外,'URIs, Addressability, and the use of HTTP GET and POST’這篇文章我翻譯成了中文,歡迎各位閱讀並指正;翻譯水平實在有限,只能說“盡最大努力交付”。
————————————————
版權聲明:本文爲CSDN博主「元無心」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/HermitSun/article/details/89889743

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章