代码之家  ›  专栏  ›  技术社区  ›  reZach

OutputCache VaryByCustom cookie值

  •  8
  • reZach  · 技术社区  · 9 年前

    为了简单起见,这是我的方法

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

    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);
    }
    

    我可以验证我的浏览器是否具有 Z检验

    HttpResponse没有出站Cookie,因此这一点不适用: https://msdn.microsoft.com/en-us/library/system.web.httpcookie.shareable(v=vs.110).aspx

    如果给定的HttpResponse包含一个或多个出站Cookie,且Shareable设置为false(默认值),则响应的输出缓存将被抑制。这可以防止包含潜在敏感信息的Cookie缓存在响应中并发送到多个客户端。要允许缓存包含Cookie的响应,请为响应正常配置缓存,例如使用OutputCache指令或MVC的[OutputCache]属性,并将所有出站Cookie的Shareable设置为true。

    1 回复  |  直到 9 年前
        1
  •  4
  •   Barr J    9 年前

    微妙的答案是否定的。

    解释的答案如下:

    输出缓存不能很好地处理Cookie的原因

    因此,输出缓存不会缓存带有cookie的响应的原因是cookie可能是用户特定的(例如身份验证、分析跟踪等)。如果一个或多个Cookie具有该属性 HttpCookie.Shareable = false ,则输出缓存认为响应不可缓存。

    解决方案:

    不过,还有一些解决方法,输出缓存将响应头和内容缓存在一起,并且在将它们发送回用户之前不提供任何挂钩来修改这些内容。然而,有一种方法可以提供在将缓存响应的头发送回用户之前对其进行更改的能力。 .

    我有一个代码示例:

    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")
        });
    };
    

    我不知道它是否回答了你的问题,但我希望它指向了正确的方向。

    推荐文章