故事要從點部落看到的一篇文章說起,該文章引薦了另一篇文章: 如何有效的提升網站的效能 - 12項建議,提出不少網頁前端設計上的效能提示,不過其中第8點:

在使用Ajax時,請盡量以GET的方式進行請求,以POST的方式將造成雙倍的要求次數

卻讓人有些疑惑,依我對HTTP GET與POST運作原理的理解,無法解釋Request數變兩倍的理由。於是在噗浪上討論了一番,發現許多人跟我有相同的疑問,直到JavaScript神人-費拉諾蘭大公現身,指引眾人一條明路,在Yahoo Developer Network Best Practices for Speeding Up Your Web Site一文中,曾出現類似的說法:

Use GET for AJAX Requests

tag: server

The Yahoo! Mail team found that when using XMLHttpRequest, POST is implemented in the browsers as a two-step process: sending the headers first, then sending data. So it's best to use GET, which only takes one TCP packet to send (unless you have a lot of cookies). The maximum URL length in IE is 2K, so if you send more than 2K data you might not be able to use GET.

An interesting side affect is that POST without actually posting any data behaves like GET. Based on theHTTP specs, GET is meant for retrieving information, so it makes sense (semantically) to use GET when you're only requesting data, as opposed to sending data to be stored server-side.

依該文的說法,Yahoo Mail團隊發現使用XMLHttpRequest丟出POST Request時,瀏覽器會針對Header跟Data各丟出一個封包(Packet);而GET Request則只會產生一個封包。由此猜測,前述第8點可能以該說為依據,但較精準的說法應是"以POST的方式將造成較多的封包(Packet)數",而非要求(Request)次數。

不過,我還是不能理解瀏覽器要將Header與Data拆成兩個封包的理由(連帶我對其正確性也有所懷疑),決心要親眼驗證過才甘心,於是網路茶包一哥Microsoft Network Monitor登場!!

我採取的測試方法是借用jQuery自動完成懶人包 一文的線上範例,分別用IE9、Firefox、Chrome瀏覽該網 頁,期間使用Microsoft Network Monitor監測對該網站的TCP/IP封包,接著打開IE Dev Tools, FireBug, Chrome開發者工具,在Console區各下達$.get("ACDataSrv.aspx", { q: 2})及$.post("ACDataSrv.aspx", { q: 2})指令,模擬XMLHttpRequest送出GET及POST Request的情境。

由Microsoft Network Monitor取得IE9, Firefox, Chrome的XMLHttpRequest在送出GET/POST時所傳遞的封包如下:


至於Firefox,則q=2內容會跟Header一起傳送,沒有拆成兩個封包的狀況。


Chrome亦是用一個封包送完Header及Data(q=2)。

由封包結果來看,Yahoo Mail團隊所說的POST會分成Header與Data兩個封包傳送的情況確實存在,且應來自對IE瀏覽器的觀察,但在Firefox、Chrome上並沒有發生。由以上實驗推導出一項結論: "AJAX POST確實有可能比GET產生更多TCP/IP封包,但只適用於部分瀏覽器(至少IE如此),並非一體適用",而在Yahoo的效能建議裡,點出的應該就是在TCP/IP連線中多送一個封包所造成的效能損耗。

個人覺得在已建好的TCP連線上多產生一個IP封包,造成的效能減損很有限,加上並非所有瀏覽器都會發生,因此微小到幾乎可以無視。但回到"AJAX POST是否比GET沒效率?"這個議題上,我卻認為是成立的,只是理由有點不同:

  1. POST的Header比GET大一些! 由上述觀察到的封包大小可以印證此點。IE9, Firefox, Chrome的GET及POST Request封包大小都是GET小於POST,分別為440(GET) < 505(POST), 438 < 564, 526 < 630,Firefox甚至多出120 Bytes以上,而觀察Header差異,主要來自Conent-Length, Content-Type... 等額外資料。

    以Chrome為例,GET時的Header如下:
    GET /miniajaxlab/jqueryautocomp/ACDataSrc.aspx?q=2 HTTP/1.1
    Host: www.darkthread.net
    Connection: keep-alive
    X-Requested-With: XMLHttpRequest
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.52 Safari/536.5
    Accept: */*
    Referer: http://www.darkthread.net/miniajaxlab/jqueryautocomp/default.htm
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    Accept-Charset: Big5,utf-8;q=0.7,*;q=0.3

    而POST時如下: (橘色為多出部分)
    POST /miniajaxlab/jqueryautocomp/ACDataSrc.aspx HTTP/1.1
    Host: www.darkthread.net
    Connection: keep-alive
    Content-Length: 3
    Origin: http://www.darkthread.net
    X-Requested-With: XMLHttpRequest
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.52 Safari/536.5
    Content-Type: application/x-www-form-urlencoded
    Accept: */*
    Referer: http://www.darkthread.net/miniajaxlab/jqueryautocomp/default.htm
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    Accept-Charset: Big5,utf-8;q=0.7,*;q=0.3

  2. 在某些情境下GET可以直接由Cache提取暫存內容,省下發出GET的成本,但POST每次都會重送。

由上述兩點來看,GET消耗的網路資訊的確比POST少,但我們應該要"盡量用GET取代POST"嗎? 這點我並不贊同!!

除了效能,"該用GET還是POST?"還有其他更重要的考量因素,例如: 在RESTful API中,GET/POST代表不同涵義;另外,使用GET進行AJAX更新時會衍生資安風險(延伸閱讀: 隱含殺機的GET式AJAX資料更新)... 等,很多情況該用POST,就不該拿GET來取代。

依我個人意見,大家就忘了第8點吧!


Comments

# by deded

超專業的,好像discovery頻道裡的「謠言終結者」

# by 許當麻

這篇文章超棒 , 學到學到

# by 小學生

超棒~~

# by 初學者

太專業了!

Post a comment