http post 和 get

RFC2616


POST和GET的差別其實是很大的。語義上,GET是獲取指定URL上的資源,是讀操作,重要的一點是不論對某個資源GET多少次,它的狀態是不會改變的,在這個意義上,我們說GET是安全的(不是被密碼學或者數據保護意義上的安全)。因爲GET是安全的,所以GET返回的內容可以被瀏覽器,Cache服務器緩存起來(其中還有很多細節,但不影響這裏的討論)。

而POST的語意是對指定資源“追加/添加”數據,所以是不安全的,每次提交的POST,參與的代碼都會認爲這個操作會修改操作對象資源的狀態,於是,瀏覽器在你按下F5的時候會跳出確認框,緩存服務器不會緩存POST請求返回內容。

兩個概念: 安全的(Safe)和冪等的(Idempotent)

安全的是指沒有明顯的對用戶有影響的副作用(包括修改該資源的狀態)。HTTP方法裏的GET和HEAD都是安全的。

冪等的是指一個方法不論多少次操作,結果都是一樣。PUT(把內容放到指定URL),DELETE(刪除某個URL代表的資源),雖然都修改了資源內容,但多次操作,結果是相同的,因此和HEAD,GET一樣都是冪等的。

所以根據HTTP協議,GET是安全的,也是冪等的,而POST既不是安全的,也不是冪等的。


對與通過get方式提交的url,瀏覽器在提交前首先根據http協議把一一個的參數及其值解析配對。而url的參數間是通過&分割的,這就是瀏覽器進行參數配置的分割依據。如果你的參數值中含有&等url特殊字符,那麼你在服務器端就會拿到意想不到的值。所以必須對url的特殊字符進行轉義。
編碼的格式爲:%加字符的ASCII碼,即一個百分號%,後面跟對應字符的ASCII(16進制)碼值。例如空格的編碼值是"%20"。
下表中列出了一些URL特殊符號及編碼
 

[十六進制值]
1. + URL 中+號表示空格 %2B
2. 空格 URL中的空格可以用+號或者編碼 %20
3. / 分隔目錄和子目錄 %2F
4. ? 分隔實際的 URL 和參數 %3F
5. % 指定特殊字符 %25
6. # 表示書籤 %23
7. & URL 中指定的參數間的分隔符 %26
8. = URL 中指定參數的值 %3D











1. get和post的定義

W3CHTML 4.01 specification說,form元素的method屬性用來指定發送form的HTTP方法。

  • 使用get時,form的數據集(形如control-name=current-value的鍵值對)被附加到form元素的action屬性所指定的URI後面;
  • 使用post時,form的數據集(形如control-name=current-value的鍵值對)被包裝在請求的body中並被髮送。

這可以簡單地理解爲,get僅僅是拼接一個URI,然後直接向服務器請求數據(需要提交給服務器的數據集包含在URI中)。比如:

<form method="get" action="FormGet.aspx">
    
<input type="text" name="ProductID" value="1" />
    
<input type="submit" value="Get" />
</form>

這個form在提交的時候,會產生這樣能夠一個get請求:FormGet.aspx?ProductID=1。

而post會把form的數據集,即ProductID=1這個鍵值對包裝在請求的body中,發送給服務器,然後向服務器請求數據。對於:

<form method="post" action="FormPost.aspx">
    
<input type="text" name="ProductID" value="1" />
    
<input type="submit" value="Get" />
</form>

這樣一個form在提交時,我們將看到一個乾淨的URI:FormPost.aspx。因爲數據不是拼接在URI中。

2. get和post的區別

2.1 安全性

如果用get提交一個驗證用戶名和密碼的form,一般認爲是不安全的。因爲用戶名和密碼將出現在URL上,進而出現在瀏覽器的歷史記錄中。顯然,在對安全性有要求的情況下,應該使用post。

2.2 編碼

HTML 4.01 specification指出,get只能向服務器發送ASCII字符,而post則可以發送整個ISO10646中的字符(如果同時指定enctype="multipart/form-data"的話)。

注意get和post對應的enctype屬性有區別。enctype有兩個值,默認值爲application/x-www-form-urlencoded,而另一個值multipart/form-data只能用於post。

2.3 提交的數據的長度

HTTP specification並沒有對URL長度進行限制,但是IE將請求的URL長度限制爲2083個字符,從而限制了get提交的數據長度。測試表明如果URL超出這個限制,提交form時IE不會有任何響應。其它瀏覽器則沒有URL的長度限制,因此其它瀏覽器能通過get提交的數據長度僅受限於服務器的設置。

而對於post,因爲提交的數據不在url中,所以通常可以簡單地認爲數據長度限制僅受限於服務器的設置。

2.4 緩存

由於一個get得到的結果直接對應到一個URI,所以get的結果頁面有可能被瀏覽器緩存。而post一般則不能,參考5。

2.5 引用和SEO

出於和上面相同的原因,我們可以用一個URI引用一個get的結果頁面,而post的結果則不能,所以必然不能被搜索引擎搜到。

3. 服務端的處理

在服務端的ASP.NET程序中,對於get,我們用Request.QueryString[control-name]來取得對應的=current-value;對於post,我們用Request.Form[control-name]。

我們也可以籠統地使用Request[control-name]。但這樣做的效率不如前者。我們可以用下面的程序比較Request.QueryString和Request的效率:

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    protected 
void Page_PreInit(object sender, EventArgs e)
    {
        
if(Request["InputString"!= null)
        {
            
int count = 1000000;
            DateTime start;
            DateTime end;
            string value 
= "";
            start 
= DateTime.Now;
            
for(int i = 0;i < count;i++)
            {
                value 
= Request.QueryString["InputString"];
            }
            end 
= DateTime.Now;
            
double requestGet = (end - start).TotalSeconds;
            start 
= DateTime.Now;
            
for(int i = 0;i < count;i++)
            {
                value 
= Request["InputString"];
            }
            end 
= DateTime.Now;
            
double request = (end - start).TotalSeconds;
            compare.InnerHtml 
= requestGet.ToString() + " / " + request.ToString() + " = " + (requestGet / request).ToString();
            get.InnerHtml 
= value;
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
    
<head>
        
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        
<title>Request.QueryString / Request</title>
    
</head>
    
<body>
        
<form method="get" action="FormGet.aspx">
            
<div>
                
<input type="text" name="InputString" /><input type="submit" value="Post" /><br />
                Get: 
<span runat="server" id="get"></span><br />
                Request.QueryString / Request: 
<span runat="server" id="compare"></span>
            
</div>
        
</form>
    
</body>
</html>

同樣的辦法我們可以比較Request.Form和Request。

最後得到的結果(Request.QueryString[control-name] / Request[control-name]和Request.Form[control-name] / Request[control-name])大多數時候是小於1的。因此,我們因該儘量用Request.QueryString或Request.Form來代替Request。

4. 正確地使用get和post

W3C的官方建議是:當且僅當form是冪等(idempotent)的時候,使用get。冪等是一個數學上的術語,其定義是:對於一個函數f : D -> D,如果D中的所有x滿足f (f x) = f x,那麼這個函數是冪等的。HTTP specification(比如RFC 2616)中,將冪等解釋爲:多次相同請求產生的副作用,和一次請求的副作用相同。

打個比方,如果你提交一個form會從Google上查詢一個關鍵詞,那麼我們可以認爲這個form是冪等的,因爲1次提交和10次提交的副作用是差不多的(10次查詢可能會多消耗一些電能);如果你提交一個form是訂購一個終極大黃蜂(Utimate bumblebee),那麼這就不是冪等的:要是你不小心多提交了1次form的話,你可能會被老婆亂罵,你不小心又提交了10次的話,你可能就破產了——一次提交和多次提交的副作用明顯不同,所以這不是冪等的。

所以,一般來說,如果提交這個請求純粹只是從服務端獲取數據而不進行其他操作,並且多次提交不會有明顯的副作用,應該使用get。比如:

如果提交這個請求會產生其它操作和影響,就應該使用post。比如:

  • 修改服務器上數據庫中的數據;
  • 發送一封郵件;
  • 刪除一個文件。

另一個要考慮的因素是安全性。見2.1。

5. 瀏覽器差異

  • IE 6:URL長度限制爲2083個字符;post之後,刷新頁面不會自動重新post數據,會出現警告;
     
    並且,在後退的過程中有可能出現“Page has Expired”(通常是向自己post,然後後退時):
     
    微軟的技術支持人員號稱“this is not a bug or problem specified to the ASP.NET but a security feature of the IE Browser”,並且說“You can also inform your users of this”,實在是荒唐。另外,一篇KB也提到這個問題,說將Response.CacheControl設爲"Public"即可,經測試僅在第一次後退時有效。
  • IE 7:和IE 6相同;
  • Firefox 2.0.0.11:刷新頁面不會自動重新post數據,會出現警告;
     
  • Opera 9.24:正常(自動post數據);
  • Safari 3.0.4:post之後,刷新頁面、前進、後退都不會自動重新post數據,會出現警告。
     

6. 參考



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章