﻿# 解決 OutputCacheLocation 指定為 ServerAndClient 的問題

Web 效能調校博大精深，眉眉角角的細節之多實在難以整理，我想大部分人也都跟我一樣，也是從一次又一次與茶包戰鬥中累積經驗，這次又遇到了一個有趣的問題。當我想將頁面設定輸出快取時，可能會希望同時快取在伺服器端(Server)與用戶端(Client)，因此在設定 OutputCache 時有個 [Location](http://msdn.microsoft.com/zh-tw/library/system.web.ui.outputcachelocation.aspx) 屬性，我們會將其設定為 ServerAndClient 或 Any，但是你可否知道即便你這樣設定，一樣無法快取網頁內容於用戶端(Client)，所以單單這樣設定並沒有達到我們真正想達到的目的 ( 同時在 Server 與 Client 快取網頁 )。

我簡單寫了一個測試輸出快取的網頁如下：

**ASP.NET Web Form 範例**

```xml
<%@ Page Language="C#" AutoEventWireup="true" 
         CodeBehind="OutputCacheTest.aspx.cs" 
         Inherits="MvcApplication5.OutputCacheTest" %>

<%@ OutputCache Duration="300" 
                VaryByParam="*" 
                Location="ServerAndClient" %>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <%=DateTime.Now %>
    </div>
    </form>
</body>
</html>
```

**ASP.NET MVC 範例**

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/75eb9cb4f5bd22742e43-image.png)

> **[OutputCacheLocation](http://msdn.microsoft.com/zh-tw/library/system.web.ui.outputcachelocation.aspx) 設定成 ServerAndClient 與 Any 的差別**
> 
> ServerAndClient 只會將網頁快取在 **伺服器** 與 **用戶端**
> 
> Any 除了快取在 **伺服器** 與 **用戶端** 之外還會快取網頁在 **代理伺服器端**(Proxy)

我用 [HttpWatch](http://www.httpwatch.com/) 軟體查看我網頁的 HTTP 通訊過程，發現了以下狀況 (點圖可放大)：

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/80c9df3da9bf8b9686f4-image_thumb.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/1261cd5366c3bf460a2f-image.png)

同樣一頁雖然已經設定了輸出快取，而且將 Location 屬性設定為 ServerAndClient，但是連續讀取兩次同樣的網頁卻完全沒有快取的樣子，每次都還是向伺服器取得網頁內容，你從 Result 為 200 或 Received 的位元數不為 0 即可判斷出來。

發生問題的主因就出在 ASP.NET 多送出了一個 [Vary](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44) 標頭，導致用戶端不會快取這一頁，不過這一頁確實已經在伺服器端快取了，所以網頁瀏覽速度的確會變快，不知道這樣檢視的人可能就會不知不覺的用下去，以為網頁已經達到 Web 效能調校的極限。

我看到這個問題時覺得非常詭異，這非常明顯是個 Bug，但為何到了 ASP.NET 3.5 都還沒有修正呢？不過這問題已經確定在 ASP.NET 4 修正，從 ASP.NET 4 開始不再會送出 [Vary](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44) 標頭，所以用戶端快取終於可以順利的正常運作了。

看了 [ASP.NET 4 Release Candidate (RC) Breaking Changes](http://www.asp.net/LEARN/whitepapers/aspnet4/breaking-changes/) 才發現這是有歷史包袱的，原來此問題從 ASP.NET 1.0 開始就被發現，到了 ASP.NET 1.1 的時候新增了一個 [HttpCachePolicy.SetOmitVaryStar 方法](http://msdn.microsoft.com/zh-tw/library/system.web.httpcachepolicy.setomitvarystar.aspx) 試圖解決這個問題，但你必須在 Code Behind 或 Controller 裡執行一次這個方法才能有效去除 [Vary](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44) 標頭，不過我想能看完 MSDN 文件的人應該不多吧！

到了 ASP.NET 2.0 開發團隊可能認為他們已經有 [HttpCachePolicy.SetOmitVaryStar 方法](http://msdn.microsoft.com/zh-tw/library/system.web.httpcachepolicy.setomitvarystar.aspx)可用，所以也不處理這個 Bug，到了 ASP.NET 3.5 因為直接繼承 ASP.NET 2.0 的關係所以也沒有進一步處理，直到 ASP.NET 4 這次大幅更新的時刻才決定將此問題徹底解決！

所以如果你還沒辦法升級到 ASP.NET 4 請參考以下方法：

**ASP.NET Web Form 的解決之道**

```csharp
public partial class OutputCacheTest : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.Cache.SetOmitVaryStar(true);
    }
}
```

**ASP.NET MVC 的解決之道**

```csharp
[OutputCache(Duration = 300, VaryByParam = "*",
             Location = OutputCacheLocation.Any)]
public ActionResult About()
{
    Response.Cache.SetOmitVaryStar(true);

    return View();
}
```

上述改善後的程式我們再用 [HttpWatch](http://www.httpwatch.com/) 測試一次該網頁在用戶端快取的情況，如下圖示 (點圖可放大)：

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/8b0d55ab62b65824db68-image_thumb.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/ad7a3815a5cc8a240b92-image.png)

透過上圖可知，我的網頁在第一次下載的時候花了 2.222 秒，但在第二次下載時已經完全被用戶端快取，所以瀏覽器完全沒有發出任何 HTTP Request 到伺服器端，不用從遠端下載網頁所以網頁瀏覽速度當然快，兩者速度差了近 1111 倍之多，而這也是 Web 效能調校迷人的地方！

**相關連結**

-   [ASP.NET 4 Release Candidate (RC) Breaking Changes](http://www.asp.net/LEARN/whitepapers/aspnet4/breaking-changes/)
-   [HttpCachePolicy.SetOmitVaryStar 方法](http://msdn.microsoft.com/zh-tw/library/system.web.httpcachepolicy.setomitvarystar.aspx)
-   [OutputCacheLocation 列舉型別](http://msdn.microsoft.com/zh-tw/library/system.web.ui.outputcachelocation.aspx)
-   [逐步解說：使用輸出快取功能增強網站效能](http://msdn.microsoft.com/zh-tw/library/sfw2210t.aspx)
