[Java網路安全系列面試題] GET 和 POST 的區別在哪裡?

[Java網路安全系列面試題] GET 和 POST 的區別在哪裡?

一. 概述

本文的內容源自其他部落格的總結,屬於筆者的讀書筆記,結構如下:

  • HTTP 的請求報文
  • GET 方法的特點
  • POST 方法的特點
  • GET 和 POST 的區別

二. HTTP 的請求報文

首先我們要解決的第一個問題是:GET 和 POST 是什麼?

GET 和 POST 其實都是 HTTP 的請求方法。除了這 2 個請求方法之外,HTTP 還有 HEADPUTDELETETRACECONNECTOPTIONS 這 6 個請求方法。所以HTTP 的請求方法共計有 8 種,它們的描述如下所示:

表格數據來源:菜鳥教程

請求方法 描述
GET 請求指定的頁面資訊,並返回實體主體。
POST 向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。
HEAD 類似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭。
PUT 從客戶端向伺服器傳送的數據取代指定的文檔的內容。
DELETE 請求伺服器刪除指定的頁面。
TRACE 回顯伺服器收到的請求,主要用於測試或診斷。
CONNECT HTTP/1.1協議中預留給能夠將連接改為管道方式的代理伺服器。
OPTIONS 允許客戶端查看伺服器的性能。

接下來我們解決第二個問題:請求方法如何使用?

要解決這個問題,我們首先需要了解 HTTP 的請求報文結構:
HTTP 的請求報文結構

可以看到 HTTP 的請求報文由三部分構成:

  • 請求行:由請求方法(Method)、URL 欄位和 HTTP 的協議版本組成,注意其中的空格、回車符和換行符均不可省略,所以我們的請求方法實際上就是位於請求行中的了。

  • 請求頭部:位於請求行之後,個數可以為 0~若干個,每個請求頭部都包含一個頭部欄位名和一個值,它們之間用冒號 “:” 分隔,在最後用回車符和換行符表示結束。

  • 請求數據:如果請求方法為 GET,那麼請求數據為空。它主要是在 POST 中進行使用,適用於需要填表單(FORM)的場景。
    我們通過一個實際的例子來看看 HTTPGET 請求報文是什麼樣的,我們這裡以訪問 //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

請求行下面部分全都是請求頭部,我們可以看到 hostapi.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

可以看到請求行中請求方法為 POSTURL 為空,協議版本也是 HTTP1.1 。它和上面 GET 方法例子不一樣的地方在於它的請求參數是位於請求數據中的,可以看到 name=Professional%20Ajax&publisher=Wiley 就是它的請求數據。並且我們要注意到在請求數據和請求頭之間是空出一行的,這是必不可少的。

三. GET 方法的特點

  1. 前面的例子://api.github.com/search/users?q=JakeWharton 就是一個非常典型的 **GET **請求的表現形式,即請求的數據會附在 URL 之後(放在請求行中),以 ? 分割 URL 和傳輸數據,多個參數用 & 連接。

  2. 除此之外,根據 HTTP 規範,GET 用於資訊獲取,而且應該是安全和冪等的 。

安全性: 指的是非修改資訊,即該操作用於獲取資訊而非修改資訊。換句話說,GET 請求一般不應產生副作用,也就是說,它僅僅是獲取資源資訊,就像資料庫查詢一樣,不會修改,增加數據,不會影響資源的狀態。
冪等性(Idempotence): 則指的是無論調用這個URL 多少次,都不會有不同的結果的 HTTP 方法。而在實際過程中,這個規定沒有那麼嚴格。例如在一個新聞應用中,新聞站點的頭版不斷更新,雖然第二次請求會返回不同的一批新聞,該操作仍然被認為是安全的和冪等的,因為它總是返回當前的新聞。

  1. GET 是會被瀏覽器主動快取的,如果下一次傳輸的數據相同,那麼就會返回快取中的內容,以求更快地展示數據。
  2. GET 方法的 URL 一般都具有長度限制,但是需要注意的是 HTTP 協議中並未規定 GET 請求的長度。 這個長度限制主要是由瀏覽器和 Web 伺服器所決定的,並且各個瀏覽器對長度的限制也各不相同
  3. GET 方法只產生一個 TCP 數據包,瀏覽器會把請求頭和請求數據一併發送出去,伺服器響應 200 ok(返回數據)。

四. POST 方法的特點

  1. 根據 HTTP 規範,POST 表示可能修改變伺服器上的資源的請求。例如我們在刷知乎的時候對某篇文章進行點贊,就是提交的 POST 請求,因為它改變了伺服器中的數據(該篇文章的點贊數)。

  2. POST 方法因為有可能修改伺服器上的資源,所以它是不符合安全和冪等性的。

  3. 從前面關於 POST 的請求報文也可以看出,POST 是將請求資訊放置在請求數據中的,這也是 POSTGET 的一點不那麼重要的區別。有一些部落格的說法是 GET 請求的請求資訊是放置在 URL 的而 POST 是放置在請求數據中的所以 POSTGET 更安全。其實這種說法很有問題,隨便抓下包 POST 中的請求報文就暴露無疑了,這又何來安全之說?

  4. 因為 POST 方法的請求資訊是放置在請求數據中的,所以它的請求資訊是沒有長度限制的。

  5. POST 方法會產生兩個 TCP 數據包,瀏覽器會先將請求頭髮送給伺服器,待伺服器響應100 continue,瀏覽器再發送請求數據,伺服器響應200 ok(返回數據)。這麼看起來 GET 請求的傳輸會比 POST 快上一些(因為GET 方法只發送一個 TCP 數據包),但是實際上在網路良好的情況下它們的傳輸速度基本相同。

五. GET 和 POST 的區別

上面說了那麼多 GET 方法和 POST 方法各自的特點,它們在外在的表現上似乎是有著諸多的不同,但是實際上,它們的本質是一樣的,並無區別!!!

這似乎有些不可思議,但是我們重新回想一下 GETPOST 是什麼?它們是 HTTP 請求協議的請求方法,而 HTTP 又是基於TCP/IP的關於數據如何在萬維網中如何通訊的協議,所以 GET/POST 實際上都是 TCP 鏈接。

也就是說,GETPOST 所做的事其實是一樣的,如果你給 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: //github.com/CoderMerlin/coder-programming
Gitee: //gitee.com/573059382/coder-programming
歡迎關注並star~
微信公眾號