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表單的使用方法如下:
[xhtml] view plain copy print ?
- <form action="目标位址" method="發送方式" enctype="資料主體的編碼方式">
- <!-- 各類型的表單域 -->
- <input name="NAME" value="VALUE"/>
- <textarea name="NAME">VALUE</textarea>
- <select name="NAME">
- <option value="VALUE" selected="selected"/>
- </select>
- </form>
<form action="目标位址" method="發送方式" enctype="資料主體的編碼方式">
<!-- 各類型的表單域 -->
<input name="NAME" value="VALUE"/>
<textarea name="NAME">VALUE</textarea>
<select name="NAME">
<option value="VALUE" selected="selected"/>
</select>
</form>
表單中存在各種類型的表單域标簽,如<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編碼規則。如下面的表單:
[xhtml] view plain copy print ?
- <!-- 不符合要求的表單 -->
- <form action="checkUser.html?opt=中文" method="POST">
- </form>
<!-- 不符合要求的表單 -->
<form action="checkUser.html?opt=中文" method="POST">
</form>
這樣的表單是不符合要求的。如果其URL值存在非法字元(如中文字元),應将其進行URL Encoding處理。URL Encoding的處理方法如下:
- 字母數字字元 "a" 到 "z"、"A" 到 "Z" 和 "0" 到 "9" 保持不變。
- 特殊字元 "."、"-"、"*" 和 "_" 保持不變。
- 空格字元 " " 轉換為一個加号 "+"。
- 所有其他字元都是不安全的,是以首先使用一種編碼機制将它們轉換為一個或多個位元組。然後對每個位元組用一個包含 3 個字元的字元串 "%xy" 表示,其中 xy 為該位元組的兩位十六進制表示形式。推薦的編碼機制是 UTF-8。
将“中文”兩個字元進行URL Encoding所得到的值就是“%E4%B8%AD%E6%96%87”。
是以正确的表單應該是:
[xhtml] view plain copy print ?
- <!-- 符合要求的表單 -->
- <form action="checkUser.html?opt=%E4%B8%AD%E6%96%87" method="POST">
- </form>
<!-- 符合要求的表單 -->
<form action="checkUser.html?opt=%E4%B8%AD%E6%96%87" method="POST">
</form>
發送方式
method标簽屬性指定了表單的發送方式,發送方式隻有兩種:GET及POST。
當以GET方式發送表單時,發送的HTTP請求沒有request-body部分,是以不需要指定enctype标簽屬性。
GET方式隻送出表單域中的資料,action标簽屬性中如果存在?子句,GET方式将不予處理。如下面的表單:
[xhtml] view plain copy print ?
- <form action="checkUser.html?opt=xxx" method="GET">
- <input type="text" name="username" value="yyy"/>
- <input type="text" name="age" value="zzz"/>
- <input type="submit" value="submit"/>
- </form>
<form action="checkUser.html?opt=xxx" method="GET">
<input type="text" name="username" value="yyy"/>
<input type="text" name="age" value="zzz"/>
<input type="submit" value="submit"/>
</form>
表單送出時沒有包括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處理。這個操作通常是由使用者端浏覽器完成的。如下面的表單:
[xhtml] view plain copy print ?
- <form action="checkUser.html" method="GET">
- <input type="hidden" name="opt" value="中文"/>
- <input type="text" name="username" value="yyy"/>
- <input type="text" name="age" value="zzz"/>
- <input type="submit" value="submit"/>
- </form>
<form action="checkUser.html" method="GET">
<input type="hidden" name="opt" value="中文"/>
<input type="text" name="username" value="yyy"/>
<input type="text" name="age" value="zzz"/>
<input type="submit" value="submit"/>
</form>
其中表單域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中得以保留。如下面的表單:
[xhtml] view plain copy print ?
- <form action="checkUser.html?opt=xxx" method="POST">
- <input type="text" name="username" value="yyy"/>
- <input type="text" name="age" value="zzz"/>
- <input type="submit" value="submit"/>
- </form>
<form action="checkUser.html?opt=xxx" method="POST">
<input type="text" name="username" value="yyy"/>
<input type="text" name="age" value="zzz"/>
<input type="submit" value="submit"/>
</form>
表單送出時,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中的非法字元。如下面的表單是不符合要求的:
[xhtml] view plain copy print ?
- <!-- 不符合要求的表單 -->
- <form action="checkUser.html?opt=中文" method="POST">
- <input type="text" name="username" value="yyy"/>
- <input type="text" name="age" value="zzz"/>
- <input type="submit" value="submit"/>
- </form>
<!-- 不符合要求的表單 -->
<form action="checkUser.html?opt=中文" method="POST">
<input type="text" name="username" value="yyy"/>
<input type="text" name="age" value="zzz"/>
<input type="submit" value="submit"/>
</form>
正确的表單應該是:
[xhtml] view plain copy print ?
- <form action="checkUser.html?opt=%E4%B8%AD%E6%96%87" method="POST">
- <input type="text" name="username" value="yyy"/>
- <input type="text" name="age" value="zzz"/>
- <input type="submit" value="submit"/>
- </form>
<form action="checkUser.html?opt=%E4%B8%AD%E6%96%87" method="POST">
<input type="text" name="username" value="yyy"/>
<input type="text" name="age" value="zzz"/>
<input type="submit" value="submit"/>
</form>
資料主體的編碼方式
在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處理。如下面的表單:
[xhtml] view plain copy print ?
- <form action="checkUser.html" method="POST">
- <input type="hidden" name="opt" value="中文"/>
- <input type="text" name="username" value="yyy"/>
- <input type="text" name="age" value="zzz"/>
- <inupt type="submit" value="submit"/>
- </form>
<form action="checkUser.html" method="POST">
<input type="hidden" name="opt" value="中文"/>
<input type="text" name="username" value="yyy"/>
<input type="text" name="age" value="zzz"/>
<inupt type="submit" value="submit"/>
</form>
表單送出時,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"。如下面的表單:
[xhtml] view plain copy print ?
- <form action="checkUser.html?opt=xxx" method="POST"
- enctype="multipart/form-data">
- <input type="text" name="username" value="yyy"/>
- <input type="text" name="age" value="zzz"/>
- <input type="file" name="file" />
- <inupt type="submit" value="submit"/>
- </form>
<form action="checkUser.html?opt=xxx" method="POST"
enctype="multipart/form-data">
<input type="text" name="username" value="yyy"/>
<input type="text" name="age" value="zzz"/>
<input type="file" name="file" />
<inupt type="submit" value="submit"/>
</form>
表單送出時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了。