OutputCacheAttribute 是改善 ASP.NET MVC 網站效能的利器,只需在 Action 加註 [Output] 並指定 Cache 保留時間,其他什麼都不用做就得到跑一次程式應付數十上百次請求的效能提升,能提升回應速度並降低資源消耗。至於因應不同類型請求快取多份內容,OutputCache 提供了 VaryByParam、VaryByConentEncoding、VaryByHeader... 等屬性可供應用。

我遇到一個比較刁鑽的狀況,Action 偵測 UserAgent (判斷邏輯來自Detect Mobile Browsers 網站) 針對行動裝置輸出行動版專屬內容,一開始沒留意直接套用 OutputCache,於是上演電腦版看完幾分鐘內行動裝置一直看到電腦版;若手機先瀏覽完該頁,電腦連也猛出現行動版的混亂場面,慘不忍睹。上線在即才發現這個問題,心頭涼了半截,該不會整個架構要翻吧?

這個問題的根本解決之道是針對電腦及手機各留一份 Cache,直覺該從 VaryBy* 屬性下手。由於 URL 參數完全相同 VaryByParam 不適用;內容差異與 Accept-Encoding 無關 VaryByContentEncoding 派不上用場;內容雖然與 User-Agent Header 有關,若設定 VaryByHeader="User-Agent" 變成一款瀏覽器一份 Cache 也不對。幸好設計者有想到這點,已預留自訂邏輯的彈性,最後還有一招大絕 - VaryByCustom,最後順利解決問題。

簡單介紹 VaryByCustom 的做法。第一步是先為自訂變數取個名字,例如:MobileUserAgent,宣告 [OutputCacheAttribute] 時則多加上 VaryByCustom="MobileUserAgent" 參數:

[OutputCache(CacheProfile = "Default", VaryByCustom = "MobileUserAgent")]
public ActionResult Index()
{
    //省略,偵測 User-Agent 針對行動裝置傳回行動版專屬內容
}

接下來,我們要在 Global.asax.cs 覆寫 GetVaryByCustomString(),在 arg 傳入 "MobileUserAgent" 時檢查 User-Agent Header 是否為行動裝置,若是傳回 "Y",若否傳回 "N",如此即可產生兩份 Cache 內容提供給不同類型裝置,危機解除~

public class MvcApplication : System.Web.HttpApplication
{
    public override string GetVaryByCustomString(HttpContext context, string arg)
    {
    	if (arg == "MobileUserAgent")
    	{
    		var isMobile = MobileTools.DetectMobileUserAgent(
    			Request.ServerVariables["HTTP_USER_AGENT"]);
    		return isMobile ? "Y" : "N";
    	}
    	return base.GetVaryByCustomString(context, arg);
    }

Showing how to use VaryByCustom to provide separate output cache for computers and mobile devices.


Comments

Be the first to post a comment

Post a comment