從使用者的角度看,一個HTTP請求起始于
用戶端瀏覽器上輸入的一個URL地址;
網頁中的一個超鏈接;
提交一個HTML表單。
但本質上說,一個HTTP請求起始于用戶端向HTTP服務器發送的一個URL請求。
一個標準的HTTP請求由以下幾個部分組成
<request-line> //請求行 用來說明請求類型,要訪問的資源以及http版本
<headers> //頭部信息,用來說明服務器要使用的附加信息<CRLF> // 回車換行符,用來表明頭部信息的結束
//以上部分是必須的內容,根據需喲啊可在頭部信息結束之后增加主體數據
[<request-body><CRLF>] // 是一個回車換行符,用來表明主體數據的結束
需要注意的是
請求行(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表單的使用方法如下:
表單中存在各種類型的表單域標簽,如<input/>、<textarea/>及<select/>。每一種表單域標簽均有NAME與VALUE兩種標簽屬性。這兩個標簽屬性決定了表單提交時傳送的屬性名及相應的值。
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的處理方法如下:
將“中文”兩個字符進行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處理。這個操作通常是由用戶端瀏覽器完成的。如下面的表單:
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]
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安全,用戶不能從地址欄上看到傳送的數據。