你真的了解 GET 和 POST 嗎,它們的區(qū)別是什么?
點擊上方 三分鐘學(xué)前端,關(guān)注公眾號
回復(fù)交流,加入前端編程面試算法每日一題群
引言
本文從以下幾個方面走進 GET 與 POST 的區(qū)別:
-
w3school 給出的標準答案 -
從 HTTP 是什么開始,深入 GET 與 POST 請求方法,及兩者的本質(zhì)區(qū)別 -
常見的 GET 與 POST 問題與誤解 -
POST 方法比 GET 方法安全? -
POST 方法會產(chǎn)生兩個 TCP 數(shù)據(jù)包?
標準答案
GET 與 POST 是 HTTP 請求中最常用的兩種方法,GET 與 POST 的區(qū)別也是老生常談的問題了,信手拈來
| GET | POST | |
|---|---|---|
| 后退按鈕/刷新 | 無害 | 數(shù)據(jù)會被重新提交(瀏覽器應(yīng)該告知用戶數(shù)據(jù)會被重新提交)。 |
| 書簽 | 可收藏為書簽 | 不可收藏為書簽 |
| 緩存 | 能被緩存 | 不能緩存 |
| 編碼類型 | application/x-www-form-urlencoded | application/x-www-form-urlencoded 或 multipart/form-data。為二進制數(shù)據(jù)使用多重編碼。 |
| 歷史 | 參數(shù)保留在瀏覽器歷史中。 | 參數(shù)不會保存在瀏覽器歷史中。 |
| 對數(shù)據(jù)長度的限制 | 是的。當(dāng)發(fā)送數(shù)據(jù)時,GET 方法向 URL 添加數(shù)據(jù);URL 的長度是受限制的(URL 的最大長度是 2048 個字符)。 | 無限制。 |
| 對數(shù)據(jù)類型的限制 | 只允許 ASCII 字符。 | 沒有限制。也允許二進制數(shù)據(jù)。 |
| 安全性 | 與 POST 相比,GET 的安全性較差,因為所發(fā)送的數(shù)據(jù)是 URL 的一部分。在發(fā)送密碼或其他敏感信息時絕不要使用 GET ! | POST 比 GET 更安全,因為參數(shù)不會被保存在瀏覽器歷史或 web 服務(wù)器日志中。 |
| 可見性 | 數(shù)據(jù)在 URL 中對所有人都是可見的。 | 數(shù)據(jù)不會顯示在 URL 中。 |
上面是 w3school 給出的標準答案
但你真的理解它嗎?在我們學(xué)習(xí)了那么多 HTTP 知識后,僅僅回答這些就夠了嗎?GET 與 POST 都是 HTTP 的請求方法,如何理解請求方法?本質(zhì)區(qū)別又是什么?
下面讓我們一步步走進 GET 與 POST 方法,以及兩者的本質(zhì)區(qū)別
深入 GET 與 POST 請求方法
1. HTTP 是什么?
HTTP (HyperText Transfer Protocol)是建立在 TCP 上的應(yīng)用層協(xié)議,超文本傳輸協(xié)議。其中:
-
超文本:圖片、音頻、視頻、甚至是壓縮包等 -
傳輸:兩點之間數(shù)據(jù)的雙向傳送 -
協(xié)議:一種行為約定和規(guī)范
所以,HTTP 協(xié)議用更通俗易懂的話描述就是 一個在計算機世界里專門在兩點之間傳輸文字、圖片、音頻、視頻等超文本數(shù)據(jù)的約定和規(guī)范
雖說 HTTP 協(xié)議是“傳輸協(xié)議”,但它不關(guān)心尋址、路由、數(shù)據(jù)完整性等傳輸細節(jié),這些底層的具體傳輸工作是由 TCP/IP 協(xié)議負責(zé),例如 IP 協(xié)議實現(xiàn)尋址和路由、TCP 協(xié)議實現(xiàn)可靠數(shù)據(jù)傳輸,另外還有 DNS 協(xié)議實現(xiàn)域名查找、SSL/TLS 協(xié)議實現(xiàn)安全通信等
那 HTTP 協(xié)議主要干嘛喃?
2. HTTP 報文
HTTP 協(xié)議的核心部分就是它定義的傳輸報文的格式,例如報文的組成、解析規(guī)則等,以便于在 TCP/IP 上實現(xiàn)更多樣靈活的功能,如緩存控制、數(shù)據(jù)編碼、內(nèi)容協(xié)議等
HTTP 報文分為四部分:
-
起始行:在請求報文中是請求行,在響應(yīng)報文中是狀態(tài)行(表示服務(wù)器的響應(yīng)狀態(tài)) -
頭部:header -
空行:在 header 與 body 之間其實是有個 “空行” 的 -
實體:我們通常說的 body
注意:此報文中最后是一個空白行結(jié)束,沒有 body(GET 請求一般都沒有 body)
其中,請求方法就規(guī)定在起始行中:
-
請求行:GET /uri HTTP/1.1,包含請求方法 GET 或 POST 等
-
狀態(tài)行:HTTP/1.1 200 OK,僅僅包含服務(wù)器的響應(yīng)狀態(tài),不包含請求方法
請求方法
客戶端發(fā)起 HTTP 請求,服務(wù)器響應(yīng)客戶端請求,客戶端可以對服務(wù)器端的資源進行操作,例如查詢、添加、刪除等,但具體執(zhí)行哪種操作喃?
這就是請求方法存在的意義,它規(guī)定了客戶端的某種操作指令,用來告訴服務(wù)器端我需要進行哪種操作,常見的請求方法有:
-
GET :獲取資源,常用于讀取或下載資源 -
HEAD :請求一個與 GET 請求的響應(yīng)相同的響應(yīng),只返回請求頭,沒有響應(yīng)體,多數(shù)由 JavaScript 發(fā)起 -
POST :用于將實體(body)提交到指定的資源,通常導(dǎo)致狀態(tài)或服務(wù)器上的副作用的更改 -
PUT :用請求有效載荷替換目標資源的所有當(dāng)前表示。 -
DELETE :刪除指定的資源。 -
CONNECT :建立一個到由目標資源標識的服務(wù)器的隧道,多用于 HTTPS 和 WebSocket 。 -
OPTIONS :預(yù)檢,用于描述目標資源的通信選項。通過該請求來知道服務(wù)端是否允許跨域請求。 -
TRACE :沿著到目標資源的路徑執(zhí)行一個消息環(huán)回測試,多數(shù)線上服務(wù)都不支持 -
PATCH :用于對資源應(yīng)用部分修改。
GET 請求方法應(yīng)該是 HTTP 所有請求方法中最開始出現(xiàn)的了,它表示從服務(wù)器獲取資源
POST 請求方法是 HTTP 所有協(xié)議中除 GET 之外最常使用的請求方法了,它表示向指定的服務(wù)器資源提交數(shù)據(jù),提交數(shù)據(jù)存放在 HTTP 報文中的 body 中,通常導(dǎo)致狀態(tài)或服務(wù)器上的副作用的更改
3. GET 與 POST 請求方法的本質(zhì)區(qū)別
綜上所述,總結(jié)一下,GET 與 POST 的本質(zhì)區(qū)別有兩點:
-
請求行不同: -
GET:GET /uri HTTP/1.1 -
POST:POST /uri HTTP/1.1 -
對服務(wù)器資源的操作不同: -
GET:表示從服務(wù)器獲取資源 -
POST:向指定的服務(wù)器資源提交數(shù)據(jù)(通常導(dǎo)致狀態(tài)或服務(wù)器上的副作用的更改)
進階:常見問題及解答
1. POST 方法比 GET 方法安全?
在 HTTP 協(xié)議里,所謂的“安全”是指請求方法不會對服務(wù)器上的資源進行修改,“破壞”服務(wù)器上的資源
按照這種定義,GET 請求方法是安全的,它對服務(wù)器資源執(zhí)行的僅僅是只讀操作,也是冪等的
冪等指多次執(zhí)行相同的操作,結(jié)果也都是相同的,即多次“冪”后結(jié)果“相等”
POST 請求方法是不安全的,它會修改服務(wù)器上的資源,在 RFC 里的語義,POST 是指“新增或提交數(shù)據(jù)”,多次提交數(shù)據(jù)會創(chuàng)建多個資源,所以不是冪等的
總結(jié):
-
GET:安全,冪等 -
POST:不安全,不冪等
對于傳輸來說,GET 和 POST 報文在傳輸上都是不安全的,因為 HTTP 在網(wǎng)絡(luò)上是明文傳輸?shù)模胍踩珎鬏斁偷眉用埽簿褪?HTTPS
2. POST 方法會產(chǎn)生兩個 TCP 數(shù)據(jù)包?
這個就神奇了,在部分文章中提到,POST 請求方法會將 header 和 body 分開發(fā)送,先發(fā)送 header,服務(wù)端返回 100 狀態(tài)碼再發(fā)送 body ?????????
HTTP 協(xié)議中沒有明確說明 POST 會產(chǎn)生兩個 TCP 數(shù)據(jù)包,而且實際測試(Chrome、Firefox)發(fā)現(xiàn),header 和 body 不會分開發(fā)送。
但為什么有些作者會這樣寫喃?我查找了相關(guān)資料,終于發(fā)現(xiàn),真有這種情況,原文在這里:
In search of performance - how we shaved 200ms off every POST request
https://gocardless.com/blog/in-search-of-performance-how-we-shaved-200ms-off-every-post-request/
主要內(nèi)容是作者發(fā)現(xiàn) POST 比 GET 多 200ms,然后深入研究,發(fā)現(xiàn) ruby 的 net::HTTP 庫,會將一個 http 請求拆分,先發(fā)送 header 部分。另外,由于沒有設(shè)置 TCP_NODELY ,所以第一個包之后要等待 ack ,才發(fā)下一個包,導(dǎo)致了一個請求有 200ms 的延遲。
另外,關(guān)于 HTTP 100 Continue:
100 Continue的目的是對HTTP客戶端應(yīng)用程序有一個實體的主體部分要發(fā)送服務(wù)器,但希望在發(fā)送之前查看一下服務(wù)器是否會接受這個實體這種情況進行優(yōu)化
----《HTTP權(quán)威指南》
客戶端:
如果客戶端在向服務(wù)器發(fā)送一個實體,并愿意在發(fā)送實體之前等待100 Continue響應(yīng),那么客戶端就要發(fā)送一個攜帶了值為100 Continue的Expect請求首部。如果客戶端沒有發(fā)送實體,就不應(yīng)該發(fā)送100 Continue Expect首部,因為這樣會使服務(wù)器誤以為客戶端要發(fā)送一個實體
服務(wù)器端:
如果服務(wù)器收到一條帶有值為100 Continue的Expect首部的請求,它會用100 Continue響應(yīng)或一條錯誤碼來進行響應(yīng)。服務(wù)器永遠也不應(yīng)該向沒有發(fā)送100 Continue期望的客戶端發(fā)送100 Continue狀態(tài)碼。
如果服務(wù)器在有機會發(fā)送100 Continue響應(yīng)之前就收到了部分(或者全部)的實體,說明服務(wù)器已經(jīng)打算繼續(xù)發(fā)送數(shù)據(jù)了,這樣服務(wù)器就不需要發(fā)送這個狀態(tài)碼了,但是服務(wù)器完成請求之后,還是應(yīng)該為請求發(fā)送一個最終狀態(tài)碼
也就是說,沒收到客戶端的 100 Continue 就不會有響應(yīng)
總結(jié)
因此,大多數(shù)框架都是盡量在一個 TCP 包里面把 HTTP 請求發(fā)出去的,但是也確實存在先發(fā) HTTP 頭,然后發(fā) body 的框架。但是具體發(fā)多少個TCP包,這個 不是 HTTP 協(xié)議的事情是操作系統(tǒng) TCP 協(xié)議棧與代碼的問題,跟 HTTP 沒關(guān)系
