一. 概述
本文的內容源自其他博客的總結,屬於筆者的讀書筆記,結構如下:
- HTTP 的請求報文
- GET 方法的特點
- POST 方法的特點
- GET 和 POST 的區別
二. HTTP 的請求報文
首先我們要解決的第一個問題是:GET 和 POST 是什么?
GET 和 POST 其實都是 HTTP 的請求方法。除了這 2 個請求方法之外,HTTP 還有 HEAD 、PUT 、DELETE、TRACE、CONNECT、OPTIONS 這 6 個請求方法。所以HTTP 的請求方法共計有 8 種,它們的描述如下所示:
表格數據來源:菜鳥教程
請求方法 | 描述 |
---|---|
GET | 請求指定的頁面信息,並返回實體主體。 |
POST | 向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。 |
HEAD | 類似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭。 |
PUT | 從客戶端向服務器傳送的數據取代指定的文檔的內容。 |
DELETE | 請求服務器刪除指定的頁面。 |
TRACE | 回顯服務器收到的請求,主要用於測試或診斷。 |
CONNECT | HTTP/1.1協議中預留給能夠將連接改為管道方式的代理服務器。 |
OPTIONS | 允許客戶端查看服務器的性能。 |
接下來我們解決第二個問題:請求方法如何使用?
要解決這個問題,我們首先需要了解 HTTP 的請求報文結構:
可以看到 HTTP 的請求報文由三部分構成:
-
請求行:由請求方法(Method)、URL 字段和 HTTP 的協議版本組成,注意其中的空格、回車符和換行符均不可省略,所以我們的請求方法實際上就是位於請求行中的了。
-
請求頭部:位於請求行之后,個數可以為 0~若干個,每個請求頭部都包含一個頭部字段名和一個值,它們之間用冒號 ":" 分隔,在最后用回車符和換行符表示結束。
-
請求數據:如果請求方法為 GET,那么請求數據為空。它主要是在 POST 中進行使用,適用於需要填表單(FORM)的場景。
我們通過一個實際的例子來看看 HTTP 的 GET 請求報文是什么樣的,我們這里以訪問https://api.github.com/search/users?q=JakeWharton
為例,通過抓包我們得到的請求報文如下所示:
GET /search/users?q=JakeWharton HTTP/1.1
Host: api.github.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _octo=GH1.1.1623908978.1549006668; _ga=GA1.2.548087391.1549006688; logged_in=yes; dotcom_user=GoMarck; _gid=GA1.2.17634150.1554639136; _gat=1
我們重點看到請求行:
GET /search/users?q=JakeWharton HTTP/1.1
可以看到請求方法用的是 GET 請求,URL為 /search/users?q=JakeWharton
,協議為 HTTP1.1。
請求行下面部分全都是請求頭部,我們可以看到 host 為 api.github.com
,連接方式為長連接等信息。值得注意的是我們這個例子中是不存在請求數據的。
接下來我們在來看一下 POST 請求的報文(該例子源自其他博客):
POST / HTTP/1.1
Host: www.wrox.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: Keep-Alive
name=Professional%20Ajax&publisher=Wiley
可以看到請求行中請求方法為 POST,URL 為空,協議版本也是 HTTP1.1 。它和上面 GET 方法例子不一樣的地方在於它的請求參數是位於請求數據中的,可以看到 name=Professional%20Ajax&publisher=Wiley 就是它的請求數據。並且我們要注意到在請求數據和請求頭之間是空出一行的,這是必不可少的。
三. GET 方法的特點
-
前面的例子:
https://api.github.com/search/users?q=JakeWharton
就是一個非常典型的 **GET **請求的表現形式,即請求的數據會附在 URL 之后(放在請求行中),以 ? 分割 URL 和傳輸數據,多個參數用 & 連接。 -
除此之外,根據 HTTP 規范,GET 用於信息獲取,而且應該是安全和冪等的 。
安全性: 指的是非修改信息,即該操作用於獲取信息而非修改信息。換句話說,GET 請求一般不應產生副作用,也就是說,它僅僅是獲取資源信息,就像數據庫查詢一樣,不會修改,增加數據,不會影響資源的狀態。
冪等性(Idempotence): 則指的是無論調用這個URL 多少次,都不會有不同的結果的 HTTP 方法。而在實際過程中,這個規定沒有那么嚴格。例如在一個新聞應用中,新聞站點的頭版不斷更新,雖然第二次請求會返回不同的一批新聞,該操作仍然被認為是安全的和冪等的,因為它總是返回當前的新聞。
- GET 是會被瀏覽器主動緩存的,如果下一次傳輸的數據相同,那么就會返回緩存中的內容,以求更快地展示數據。
- GET 方法的 URL 一般都具有長度限制,但是需要注意的是 HTTP 協議中並未規定 GET 請求的長度。 這個長度限制主要是由瀏覽器和 Web 服務器所決定的,並且各個瀏覽器對長度的限制也各不相同 。
- GET 方法只產生一個 TCP 數據包,瀏覽器會把請求頭和請求數據一並發送出去,服務器響應 200 ok(返回數據)。
四. POST 方法的特點
-
根據 HTTP 規范,POST 表示可能修改變服務器上的資源的請求。例如我們在刷知乎的時候對某篇文章進行點贊,就是提交的 POST 請求,因為它改變了服務器中的數據(該篇文章的點贊數)。
-
POST 方法因為有可能修改服務器上的資源,所以它是不符合安全和冪等性的。
-
從前面關於 POST 的請求報文也可以看出,POST 是將請求信息放置在請求數據中的,這也是 POST 和 GET 的一點不那么重要的區別。有一些博客的說法是 GET 請求的請求信息是放置在 URL 的而 POST 是放置在請求數據中的所以 POST 比 GET 更安全。其實這種說法很有問題,隨便抓下包 POST 中的請求報文就暴露無疑了,這又何來安全之說?
-
因為 POST 方法的請求信息是放置在請求數據中的,所以它的請求信息是沒有長度限制的。
-
POST 方法會產生兩個 TCP 數據包,瀏覽器會先將請求頭發送給服務器,待服務器響應
100 continue
,瀏覽器再發送請求數據,服務器響應200 ok
(返回數據)。這么看起來 GET 請求的傳輸會比 POST 快上一些(因為GET 方法只發送一個 TCP 數據包),但是實際上在網絡良好的情況下它們的傳輸速度基本相同。
五. GET 和 POST 的區別
上面說了那么多 GET 方法和 POST 方法各自的特點,它們在外在的表現上似乎是有着諸多的不同,但是實際上,它們的本質是一樣的,並無區別!!!
這似乎有些不可思議,但是我們重新回想一下 GET 和 POST 是什么?它們是 HTTP 請求協議的請求方法,而 HTTP 又是基於TCP/IP的關於數據如何在萬維網中如何通信的協議,所以 GET/POST 實際上都是 TCP 鏈接。
也就是說,GET 和 POST 所做的事其實是一樣的,如果你給 GET 加上請求數據,給 POST 加上 URL 參數,這在技術上是完全可行的,事實上確實有一些人為了貪圖方便在更新資源時用了GET,因為用POST必須要到FORM(表單),這樣會麻煩一點(但是強烈不建議這樣子做!!!)。
既然 GET 和 POST 的底層都是 TCP,那么為什么 HTTP 還要特別將它們區分出來呢?
其實可以想象一下,如果我們直接使用 TCP 進行數據的傳輸,那么無論是單純獲取資源的請求還是修改服務器資源的請求在外觀上看起來都是 TCP 鏈接,這樣就非常不利於進行管理。所以在 HTTP 協議中,就會對這些不同的請求設置不同的類別進行管理,例如單純獲取資源的請求就規定為 GET、修改服務器資源的請求就規定為 POST,並且也對它們的請求報文的格式做出了相應的要求(例如請求參數 GET 位於 URL 而 POST 則位於請求數據中)。
當然,如果我們想將 GET 的請求參數放置在請求數據中或者將 POST 的請求數據放置在 URL 中,這是完全可以的,雖然這樣子做並不符合 HTTP 的規范。但是這樣子做是否能得到我們期望的響應數據呢?答案是未必,這取決於服務器的行為。
以 GET 方法在請求數據中放置請求參數為例,有些服務器會將請求數據中的參數讀出,在這種情況下我們依然能獲得我們期望的響應數據;而有些服務器則會選擇直接忽略,這種情況下我們就無法獲取期望的響應數據了。
所以,對於 GET 和 POST 的區別,總結來說就是:它們的本質都是 TCP 鏈接,並無區別。但是由於 HTTP 的規定以及瀏覽器/服務器的限制,導致它們在應用過程中可能會有所不同。
推薦
文末
歡迎關注個人微信公眾號:Coder編程
歡迎關注Coder編程公眾號,主要分享數據結構與算法、Java相關知識體系、框架知識及原理、Spring全家桶、微服務項目實戰、DevOps實踐之路、每日一篇互聯網大廠面試或筆試題以及PMP項目管理知識等。更多精彩內容正在路上~
新建了一個qq群:315211365,歡迎大家進群交流一起學習。謝謝了!也可以介紹給身邊有需要的朋友。
文章收錄至
Github: https://github.com/CoderMerlin/coder-programming
Gitee: https://gitee.com/573059382/coder-programming
歡迎關注並star~