天天看點

HTTP協定與HTTP表單傳輸格式,再論get、post方法

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 ?

  1. <form action="目标位址" method="發送方式" enctype="資料主體的編碼方式">  
  2.     <!-- 各類型的表單域 -->  
  3.     <input name="NAME" value="VALUE"/>  
  4.     <textarea name="NAME">VALUE</textarea>  
  5.     <select name="NAME">  
  6.         <option value="VALUE" selected="selected"/>  
  7.     </select>  
  8. </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 ?

  1. <!-- 不符合要求的表單 -->  
  2. <form action="checkUser.html?opt=中文" method="POST">  
  3. </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 ?

  1. <!-- 符合要求的表單 -->  
  2. <form action="checkUser.html?opt=%E4%B8%AD%E6%96%87" method="POST">  
  3. </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 ?

  1. <form action="checkUser.html?opt=xxx" method="GET">  
  2.     <input type="text" name="username" value="yyy"/>  
  3.     <input type="text" name="age" value="zzz"/>  
  4.     <input type="submit" value="submit"/>  
  5. </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 ?

  1. <form action="checkUser.html" method="GET">  
  2.     <input type="hidden" name="opt" value="中文"/>  
  3.     <input type="text" name="username" value="yyy"/>  
  4.     <input type="text" name="age" value="zzz"/>  
  5.     <input type="submit" value="submit"/>  
  6. </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 ?

  1. <form action="checkUser.html?opt=xxx" method="POST">  
  2.     <input type="text" name="username" value="yyy"/>  
  3.     <input type="text" name="age" value="zzz"/>  
  4.     <input type="submit" value="submit"/>  
  5. </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 ?

  1. <!-- 不符合要求的表單 -->  
  2. <form action="checkUser.html?opt=中文" method="POST">  
  3.     <input type="text" name="username" value="yyy"/>  
  4.     <input type="text" name="age" value="zzz"/>  
  5.     <input type="submit" value="submit"/>  
  6. </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 ?

  1. <form action="checkUser.html?opt=%E4%B8%AD%E6%96%87" method="POST">  
  2.     <input type="text" name="username" value="yyy"/>  
  3.     <input type="text" name="age" value="zzz"/>  
  4.     <input type="submit" value="submit"/>  
  5. </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 ?

  1. <form action="checkUser.html" method="POST">  
  2.     <input type="hidden" name="opt" value="中文"/>  
  3.     <input type="text" name="username" value="yyy"/>  
  4.     <input type="text" name="age" value="zzz"/>  
  5.     <inupt type="submit" value="submit"/>  
  6. </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 ?

  1. <form action="checkUser.html?opt=xxx" method="POST"   
  2.         enctype="multipart/form-data">  
  3.     <input type="text" name="username" value="yyy"/>  
  4.     <input type="text" name="age" value="zzz"/>  
  5.     <input type="file" name="file" />  
  6.     <inupt type="submit" value="submit"/>  
  7. </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了。

繼續閱讀