2017-08-31 97 views
7

有沒有方法根據cookie值設置OutputCache的值?OutputCache VaryByCustom cookie值

對於純樸的緣故,這是我的方法

[OutputCache(Duration = 600, VaryByParam = "None", VaryByCustom = "ztest")] 
public ViewResult Index() 
{ 
    return View(); 
} 

的Global.asax有這個(爲了覆蓋GetVaryByCustomString方法

public override string GetVaryByCustomString(HttpContext context, string custom) 
{ 
    if (custom == "ztest") 
    { 
     HttpCookie ztest = context.Request.Cookies["ztest"]; 
     if (ztest != null) 
     { 
      return ztest.Value; 
     } 
    } 

    return base.GetVaryByCustomString(context, custom); 
} 

我可以確認我的瀏覽器有ztest cookie,但是當我調試Index方法時,我每次都會觸發斷點(意思是緩存不起作用)

獲取HttpResponse沒有出站餅乾,所以這點並不適用:https://msdn.microsoft.com/en-us/library/system.web.httpcookie.shareable(v=vs.110).aspx

如果有可共享設置爲false(默認值)給出的HttpResponse包含一個或多個出站餅乾,輸出緩存會壓制迴應。這可以防止包含潛在敏感信息的cookie被緩存在響應中併發送到多個客戶端。要允許緩存包含Cookie的響應,請正常爲響應配置緩存,例如使用OutputCache指令或MVC的[OutputCache]屬性,並將所有出站Cookie設置爲將Shareable設置爲true。

+0

你試過檢查'HttpCookie.Shareable = true'嗎? ,在你的情況下,它會像'cookie。可共享= true; ' – Webruster

+0

您的意思是檢查'GetVaryByCustomString'方法中的Shareable值是否爲真? – Zac

+0

首先檢查通常默認情況下它會'false',嘗試更改爲'true' – Webruster

回答

4

微妙的答案是否定的。

的解釋答案如下:

原因輸出緩存沒有發揮好與cookies

所以輸出緩存將不緩存與cookies響應的原因是,一個cookie可能是用戶特定的(例如認證,分析跟蹤等)。如果一個或多個cookie屬性爲HttpCookie.Shareable = false,則輸出緩存會將響應視爲不可緩存。

解決方案:

有一些變通辦法雖然,輸出緩存緩存響應頭和內容一起,並且不提供任何鉤子把他們回user.However之前修改這些,還有一種提供在緩存響應標題發送回用戶之前進行更改的功能的方法。 其中之一需要Fasterflect nuget包

我的代碼示例:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Runtime.Caching; 
using System.Web; 
using System.Web.Caching; 
using Fasterflect; 

namespace CustomOutputCache 
{ 
    /// <summary> 
    /// An output cache provider that has ability to modify the http header collection before a cached response is served back to the user. 
    /// </summary> 
    public class HeaderModOutputCacheProvider : OutputCacheProvider 
    { 
     private static readonly Type OutputCacheEntryType, HttpCachePolicySettingsType; 
     private static readonly Type[] ParameterTypes; 

     public static event EventHandler<CachedRequestEventArgs> RequestServedFromCache; 

     static HeaderModOutputCacheProvider() 
     { 
      var systemWeb = typeof(HttpContext).Assembly; 
      OutputCacheEntryType = systemWeb.GetType("System.Web.Caching.OutputCacheEntry"); 
      HttpCachePolicySettingsType = systemWeb.GetType("System.Web.HttpCachePolicySettings"); 
      ParameterTypes = new[]{ 
       typeof(Guid), 
       HttpCachePolicySettingsType, 
       typeof(string), 
       typeof(string) , 
       typeof(string[]), 
       typeof(int), 
       typeof(string), 
       typeof(List<HeaderElement>), 
       typeof(List<ResponseElement>) 
      }; 
     } 

     private readonly ObjectCache _objectCache; 

     public HeaderModOutputCacheProvider() 
     { 
      _objectCache = new MemoryCache("output-cache"); 
     } 

     #region OutputCacheProvider implementation 

     public override object Get(string key) 
     { 
      var cachedValue = _objectCache.Get(key); 

      if (cachedValue == null) 
       return null; 

      if (cachedValue.GetType() != OutputCacheEntryType) 
       return cachedValue; 

      var cloned = CloneOutputCacheEntry(cachedValue); 

      if (RequestServedFromCache != null) 
      { 
       var args = new CachedRequestEventArgs(cloned.HeaderElements); 
       RequestServedFromCache(this, args); 
      } 

      return cloned; 
     } 

     public override object Add(string key, object entry, DateTime utcExpiry) 
     { 
      _objectCache.Set(key, entry, new CacheItemPolicy { AbsoluteExpiration = utcExpiry }); 
      return entry; 
     } 

     public override void Set(string key, object entry, DateTime utcExpiry) 
     { 
      _objectCache.Set(key, entry, new CacheItemPolicy { AbsoluteExpiration = utcExpiry }); 
     } 

     public override void Remove(string key) 
     { 
      _objectCache.Remove(key); 
     } 

     #endregion 

     private IOutputCacheEntry CloneOutputCacheEntry(object toClone) 
     { 
      var parameterValues = new[] 
      { 
       toClone.GetFieldValue("_cachedVaryId", Flags.InstancePrivate), 
       toClone.GetFieldValue("_settings", Flags.InstancePrivate), 
       toClone.GetFieldValue("_kernelCacheUrl", Flags.InstancePrivate), 
       toClone.GetFieldValue("_dependenciesKey", Flags.InstancePrivate), 
       toClone.GetFieldValue("_dependencies", Flags.InstancePrivate), 
       toClone.GetFieldValue("_statusCode", Flags.InstancePrivate), 
       toClone.GetFieldValue("_statusDescription", Flags.InstancePrivate), 
       CloneHeaders((List<HeaderElement>)toClone.GetFieldValue("_headerElements", Flags.InstancePrivate)), 
       toClone.GetFieldValue("_responseElements", Flags.InstancePrivate) 
      }; 

      return (IOutputCacheEntry)OutputCacheEntryType.CreateInstance(
       parameterTypes: ParameterTypes, 
       parameters: parameterValues 
      ); 
     } 

     private List<HeaderElement> CloneHeaders(List<HeaderElement> toClone) 
     { 
      return new List<HeaderElement>(toClone); 
     } 
    } 

    public class CachedRequestEventArgs : EventArgs 
    { 
     public CachedRequestEventArgs(List<HeaderElement> headers) 
     { 
      Headers = headers; 
     } 
     public List<HeaderElement> Headers { get; private set; } 

     public void AddCookies(HttpCookieCollection cookies) 
     { 
      foreach (var cookie in cookies.AllKeys.Select(c => cookies[c])) 
      { 
       //more reflection unpleasantness :(
       var header = cookie.CallMethod("GetSetCookieHeader", Flags.InstanceAnyVisibility, HttpContext.Current); 
       Headers.Add(new HeaderElement((string)header.GetPropertyValue("Name"), (string)header.GetPropertyValue("Value"))); 
      } 
     } 
    } 
} 

電線它是這樣的:

<system.web> 
    <caching> 
     <outputCache defaultProvider="HeaderModOutputCacheProvider"> 
     <providers> 
      <add name="HeaderModOutputCacheProvider" type="CustomOutputCache.HeaderModOutputCacheProvider"/> 
     </providers> 
     </outputCache> 
    </caching> 
    </system.web> 

而且使用這種方式:

HeaderModOutputCacheProvider.RequestServedFromCache += RequestServedFromCache; 

HeaderModOutputCacheProvider.RequestServedFromCache += (sender, e) => 
{ 
    e.AddCookies(new HttpCookieCollection 
    { 
     new HttpCookie("key", "value") 
    }); 
}; 

我不知道它回答你的問題,但我希望它指向正確的方向。

+0

我沒有最終使用您的解決方案,而是最終從取決於cookie值的代碼中移除了'OutputCache' – Zac