HTTP協議與HTML表單(再談GET與POST的區別)

Creative Commons License
本作品採用知識共享署名-非商業性使用-相同方式共享 2.5 中國大陸許可協議進行許可。

 

 

HTTP的GET/POST方式有何區別?這是一個老生常談的問題,但老生常談的問題往往有一些讓人誤解的結論。本文將帶您淺嘗HTTP協議,在瞭解HTTP協議的同時將會展示許多被人們忽視的內容。在掌握了HTTP協議的過程中我們將自然而然地瞭解到GET與POST的本質區別。

 

 

HTTP請求

從使用者的角度看,一個HTTP請求起始於
用戶端瀏覽器上輸入的一個URL地址;
網頁中的一個超鏈接;
提交一個HTML表單。
但本質上說,一個HTTP請求起始於用戶端向HTTP服務器發送的一個URL請求。

一個標準的HTTP請求由以下幾個部分組成

<request-line>
<headers>
<CRLF>
[<request-body><CRLF>]

在HTTP請求中,第一行是請求行(request-line),用來說明請求類型、要訪問的資源(URL)以及使用的HTTP版本;
緊接着是多行頭部(headers)信息,用來說明服務器要使用的附加信息;
頭部信息之後是一個回車換行符(/r/n),用於標明頭部信息的結束。
以上是必須內容,根據需要可在頭部信息結束之後增加主體數據(request-body);
主體數據之後是一個回車換行符(/r/n),用於標明主體數據的結束。

 

需要注意的是
請求行(request-line)中的URL部分必須以application/x-www-form-urlencoded方式編碼。
主體數據(request-body)的編碼方式由頭部(headers)信息中的Content-Type指定。
主體數據(request-body)的長度由頭部(headers)信息中的Content-Length指定。

 

例如,我們可以在IE瀏覽器上輸入下面的網址:

http://localhost:8000/hello/index.html

HTTP請求的頭部信息如下:

GET /hello/index.html HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
Host: localhost:8000
Connection: Keep-Alive
Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6

 

[End]

上述信息沒有request-body部分,這是以GET方式發送的HTTP請求。如果請求中需要附加主體數據,即增加request-body部分,則必須使用POST方式發送HTTP請求。HTML超鏈接(<a></a>)只能用GET方式提交HTTP請求,HTML表單(<form></form>)則可以使用兩種方式提交HTTP請求。

 

 

HTML表單

HTML表單的使用方法如下:

表單中存在各種類型的表單域標籤,如<input/>、<textarea/>及<select/>。每一種表單域標籤均有NAME與VALUE兩種標籤屬性。這兩個標籤屬性決定了表單提交時傳送的屬性名及相應的值。

目標地址(URL)

action標籤屬性指定了表單提交的目標地址,其值可以是完整的URL。如:

<form action="http://localhost:8000/hello/checkUser.html"></form>

如果放置表單的網頁與表單提交的目標地址在同一個HTTP服務器上,則目標地址可以用絕對路徑表示(絕對路徑相對於HTTP服務器)。絕對路徑以“/”開頭,包括WEB應用上下文及請求。如:

<form action="/hello/checkUser.html"></form>

如果放置表單的網頁與表單提交的目標地址在同一個WEB應用上下文上,則目標地址可以用相對路徑表示(相對路徑相對於放置表單的網頁)。相對路徑不以“/”開頭,不包括WEB應用上下文。如:

<form action="checkUser.html"></form>

需要注意的是,action標籤屬性的值必須符合URL的要求,其編碼必須符合application/x-www-form-urlencoded編碼規則。如下面的表單:

這樣的表單是不符合要求的。如果其URL值存在非法字符(如中文字符),應將其進行URL Encoding處理。URL Encoding的處理方法如下:

  • 字母數字字符 "a" 到 "z"、"A" 到 "Z" 和 "0" 到 "9" 保持不變。
  • 特殊字符 "."、"-"、"*" 和 "_" 保持不變。
  • 空格字符 " " 轉換爲一個加號 "+"。
  • 所有其他字符都是不安全的,因此首先使用一種編碼機制將它們轉換爲一個或多個字節。然後對每個字節用一個包含 3 個字符的字符串 "%xy" 表示,其中 xy 爲該字節的兩位十六進制表示形式。推薦的編碼機制是 UTF-8。

將“中文”兩個字符進行URL Encoding所得到的值就是“%E4%B8%AD%E6%96%87”。

所以正確的表單應該是:

發送方式

method標籤屬性指定了表單的發送方式,發送方式只有兩種:GET及POST。
當以GET方式發送表單時,發送的HTTP請求沒有request-body部分,所以不需要指定enctype標籤屬性。

GET方式只提交表單域中的數據,action標籤屬性中如果存在?子句,GET方式將不予處理。如下面的表單:

表單提交時沒有包括opt屬性,HTTP頭部信息如下:

GET /hello/checkUser.html?username=yyy&age=zzz HTTP/1.1
Referer: http://localhost:8000/hello/index.html
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
Host: localhost:8000
Connection: Keep-Alive
Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6

 

[End]

需要注意的是,以GET方式提交表單時,每個表單域的NAME與VALUE要以URL的方式提交,所以每個表單域的NAME與VALUE均要進行URL Encoding處理。這個操作通常是由用戶端瀏覽器完成的。如下面的表單:

其中表單域opt的VALUE是中文字符“中文”,在表單提交時,用戶端瀏覽器會自動將其進行URL Encoding。HTTP頭部信息如下:

GET /hello/checkUser.html?opt=%E4%B8%AD%E6%96%87&username=yyy&age=zzz HTTP/1.1
Referer: http://localhost:8000/hello/index.html
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
Host: localhost:8000
Connection: Keep-Alive
Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6

 

[End]

 

當以POST方式發送表單時,表單域中的數據將作爲request-body提交,action標籤屬性中的?子句將在request-line中得以保留。如下面的表單:

表單提交時,HTTP頭部信息如下:

POST /hello/checkUser.html?opt=xxx HTTP/1.1
Referer: http://localhost:8000/hello/index.html
Accept: */*
Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8000
Content-Length: 20
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6

 

username=yyy&age=zzz
[End]

需要注意的是,以POST方式提交表單時,action標籤屬性的值必須是已經進行了URL Encoding處理之後的值,用戶端瀏覽器不會自動處理URL中的非法字符。如下面的表單是不符合要求的:

正確的表單應該是:

數據主體的編碼方式

在HTTP請求中,request-line總是以application/x-www-form-urlencoded方式編碼。enctype標籤屬性只對request-body起作用。也就是說只有在method="POST"的情況下,設置enctype才起作用。
設置enctype標籤屬性後,在HTTP請求的頭部(headers)信息中會多出一行Content-Type信息,並且request-body部分將會以Content-Type指定的MIME進行編碼。這些操作都是由客戶端瀏覽器自動完成的。

 

在沒有指定enctype標籤屬性時,表單以默認的application/x-www-form-urlencoded方式對request-body進行編碼。
如果表單域中的NAME或VALUE含有非法字符(如中文字符),客戶端瀏覽器會自動對其進行URL Encoding處理。如下面的表單:

表單提交時,HTTP頭部信息如下:

POST /hello/checkUser.html HTTP/1.1
Accept: */*
Referer: http://localhost:8000/hello/index.jsp
Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8000
Content-Length: 43
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=4EF9C5B81356481F470F3C60D9E77D94

 

opt=%E4%B8%AD%E6%96%87&username=yyy&age=zzz
[End]

 

如果表單中包含需要上傳的文件數據,則在指定method="POST"的同時還要指定enctype="multipart/form-data"。如下面的表單:

表單提交時HTTP頭部信息如下:

POST /hello/checkUser.html?opt=xxx HTTP/1.1
Accept: */*
Referer: http://localhost:8000/hello/index.html
Accept-Language: zh-cn
Content-Type: multipart/form-data; boundary=---------------------------7d931c5d043e
Accept-Encoding: gzip, deflate
Host: localhost:8000
Content-Length: 382
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=6FE3D8E365DF9FE26221A32624470D24

 

-----------------------------7d931c5d043e
Content-Disposition: form-data; name="username"

 

yyy
-----------------------------7d931c5d043e
Content-Disposition: form-data; name="age"

 

zzz
-----------------------------7d931c5d043e
Content-Disposition: form-data; name="file"; filename="C:/1.txt"
Content-Type: text/plain

 

hello
-----------------------------7d931c5d043e--

 

[End] 

GET與POST的區別

HTTP請求的GET與POST方式的本質區別可以參考hyddd《淺談HTTP中Get與Post的區別》一文中的描述,本文講述了其中比較重要的一條,那就是數據傳輸的位置不同。
GET方式在request-line中傳送數據;POST方式在request-line及request-body中均可以傳送數據。

對網上傳言的解釋

傳言1:GET方式對長度有限制;POST方式對長度沒限制。
回答:長度限制之說一方面是HTTP客戶端(如IE限定URL長度爲2083字節,opera 是4050, Netscape 是8192)的限制;另一方面服務器的實現也加入了限制(如果URL長度過長,HTTP服務器會報414錯誤)。但HTTP協議及URL官方說明均對長度限制則沒有規定。

 

傳言2:GET是從服務器上獲取數據;POST是向服務器傳送數據。
回答:GET方式就沒有向服務器傳送數據?那麼URL中的?子句送的是什麼?不論是GET還是POST,都可以向服務器傳送數據,只不過傳送數據的位置不同;不論是GET還是POST,都要從服務器上獲取數據,否則IE瀏覽器拿什麼東西給我們看呢?關鍵的問題是
GET的主要任務是獲得數據,但在獲得數據前也可以向服務器提交一些數據;
POST的主要任務是提交數據,但在提交數據之後服務器也會向用戶端返回一些顯示用的數據。

 

傳言3:GET不安全,用戶能從地址欄上看到傳送的數據;POST安全,用戶不能從地址欄上看到傳送的數據。
回答:POST方式看不到傳送的數據是因爲IE瀏覽器做了限制。如果你通過第三方工具看到了POST方式傳送的數據,你還能說POST方式是安全的嗎?理論上說GET和POST方式都不安全,要不就用不着研究HTTPS了。

參考資料:

[1]. HTTP協議(http://www.ietf.org/rfc/rfc2616.txt
[2]. HTML協議官方說明(http://www.w3.org/TR/html4/
[3]. URL標準(http://www.ietf.org/rfc/rfc1738.txt
[4]. MIME標準(http://www.ietf.org/rfc/rfc2045.txt

發佈了27 篇原創文章 · 獲贊 15 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章