我們先來一睹這個(gè)Bug的風(fēng)采!
在一個(gè).aspx文件中增加OutputCache設(shè)置,代碼如下:
復(fù)制代碼 代碼如下:
%@ OutputCache Duration="300" VaryByParam="*"%>
上面的設(shè)置表示:緩存5分鐘,根據(jù)不同的查詢字符串更新緩存。Location使用的是默認(rèn)值A(chǔ)ny,也就是可以在瀏覽器、代理服務(wù)器、Web服務(wù)器三個(gè)地方進(jìn)行緩存,在Response Headers中的體現(xiàn)就是Cache-Control:public, max-age=300。(如果你要用CDN加速,Cache-Control就要用public)。
然后,我們?cè)贔irefox瀏覽器中訪問這個(gè)頁面,并打開Firebug,見下圖:
第一次訪問,返回狀態(tài)碼為"200 OK",正常。這里Response Headers中的Vary:Accept-Encoding是因?yàn)镮IS啟用“動(dòng)態(tài)內(nèi)容壓縮”產(chǎn)生的,如果不啟用,就不會(huì)出現(xiàn)。
這時(shí)緩存應(yīng)該被建立起來了,我們按F5刷新一下瀏覽器,看一下結(jié)果,見下圖:
第二次訪問,返回狀態(tài)碼為"304 Not Modified",瀏覽器緩存生效,這也是我們期望的。
但是,請(qǐng)注意一下上圖中的Vary:*,它會(huì)讓瀏覽器的緩存失效,我們?cè)侔匆幌翭5驗(yàn)證一下。
果然,瀏覽器緩存失效,返回狀態(tài)碼變回了200 OK。緩存時(shí)間有5分鐘呢,第三次就失效了,這樣的結(jié)果顯然不是我們期望的。
上面的測(cè)試是在Web服務(wù)器上IIS啟用動(dòng)態(tài)內(nèi)容壓縮(dynamic content compression)的情況下進(jìn)行的,如果關(guān)閉動(dòng)態(tài)內(nèi)容壓縮,每次請(qǐng)求返回都是200 OK,Vary都是星號(hào)。也就是說瀏覽器游覽緩存根本沒起作用。
Bug欣賞完畢,我們進(jìn)行第二個(gè)測(cè)試。
將OutputCache的VaryByParam屬性值設(shè)置為none:
復(fù)制代碼 代碼如下:
%@ OutputCache Duration="600" VaryByParam="none"%>
測(cè)試結(jié)果顯示,瀏覽器第一次請(qǐng)求之后,接下來在緩存時(shí)間內(nèi),服務(wù)器的響應(yīng)都是"304 Not Modified",這才是我們想要的效果。
但是,在實(shí)際應(yīng)用中,我們使用VaryByParam="none"很少,用的更多的是為VaryByParam指定對(duì)應(yīng)的值。
所以這個(gè)Bug影響很大,增加了服務(wù)器負(fù)擔(dān),浪費(fèi)了帶寬。
Bug相關(guān)信息
在微軟的官方文檔ASP.NET 4 Breaking Changes中專門提到了這個(gè)bug —— "Output Caching Changes to Vary * HTTP Header":
In ASP.NET 1.0, a bug caused cached pages that specified Location="ServerAndClient" as an output–cache setting to emit a Vary:* HTTP header in the response. This had the effect of telling client browsers to never cache the page locally.
In ASP.NET 1.1, the System.Web.HttpCachePolicy.SetOmitVaryStar method was added, which you could call to suppress the Vary:* header. This method was chosen because changing the emitted HTTP header was considered a potentially breaking change at the time. However, developers have been confused by the behavior in ASP.NET, and bug reports suggest that developers are unaware of the existing SetOmitVaryStar behavior.
In ASP.NET 4, the decision was made to fix the root problem. The Vary:* HTTP header is no longer emitted from responses that specify the following directive:
%@OutputCache Location="ServerAndClient" %>
As a result, SetOmitVaryStar is no longer needed in order to suppress the Vary:* header.
In applications that specify Location="ServerAndClient" in the @ OutputCache directive on a page, you will now see the behavior implied by the name of the Location attribute's value – that is, pages will be cacheable in the browser without requiring that you call the SetOmitVaryStar method.
從上面的文檔中我們可以知道這個(gè)Bug的歷史:
在ASP.NET 1.0時(shí),如果在OutputCache中設(shè)置Location="ServerAndClient",在ASP.NET在響應(yīng)時(shí)會(huì)瀏覽器發(fā)送Vary:* HTTP header。
在ASP.NET 1.1時(shí),微軟針對(duì)這個(gè)Bug,提供一個(gè)專門的方法System.Web.HttpCachePolicy.SetOmitVaryStar(bool omit),通過SetOmitVaryStar(true)修改HTTP header,去掉Vary:*。
在ASP.NET 4時(shí),微軟鄭重地宣布從根本上解決了這個(gè)問題。
而且,文檔中提到這個(gè)bug只會(huì)出現(xiàn)在Location="ServerAndClient"時(shí)。
可是,我用ASP.NET 4的實(shí)測(cè)試情況是:不僅Location="ServerAndClient"時(shí)的Bug沒有解決,而且Location="Any"時(shí)也會(huì)出現(xiàn)同樣的Bug。
解決方法
解決方法很簡(jiǎn)單,只要用ASP.NET 1.1時(shí)代提供的System.Web.HttpCachePolicy.SetOmitVaryStar(bool omit)就能解決問題,只需在Page_Load中添加如下代碼:
復(fù)制代碼 代碼如下:
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.SetOmitVaryStar(true);
}
相關(guān)文檔
ASP.NET caching tests find a bug with VaryByParam
How to cache asp.net web site for better performance
Microsoft Connect: The ServerAndClient parameter with the OutputCache page directive does not cache on the client, without code
小結(jié)
小bug,解決方法也很簡(jiǎn)單。但是,如果你不知道這個(gè)bug,又會(huì)陷入微軟的一個(gè)騙局(之前提到一個(gè)WCF Client的騙局),不知不覺中浪費(fèi)了服務(wù)器資源與帶寬。
微軟那么有錢,有那么多天才程序員,可是Bug也很難避免,可見開發(fā)優(yōu)秀的軟件是多么具有挑戰(zhàn)性的工作!
補(bǔ)充
ASP.NET MVC 中不存在這個(gè)問題。