【问题标题】:OutputCache VaryByCustom cookie valueOutputCache VaryByCustom cookie 值
【发布时间】:2018-02-09 20:00:11
【问题描述】:

有没有办法根据 cookie 值设置 OutputCache 的值?

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

[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 没有出站 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。

【问题讨论】:

  • 你试过检查HttpCookie.Shareable = true 吗? ,在你的情况下,它就像cookie.Shareable = true;
  • 你的意思是检查GetVaryByCustomString方法中的Shareable值是否为真?
  • 先检查一般默认是false,试试改成true
  • 当我从 context.Request 中拉取 cookie 时,默认值为 false
  • 是的,会的,尝试保留它true 并验证一次

标签: c# asp.net caching cookies outputcache


【解决方案1】:

微妙的答案是否定的。

解释的答案如下:

输出缓存无法与 cookie 配合使用的原因

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

解决方案:

虽然有一些解决方法,输出缓存将响应标头和内容缓存在一起,并且在将它们发送回用户之前不提供任何挂钩来修改它们。但是,有一种方法可以提供更改之前的能力在将响应发送回用户之前缓存响应的标头。 其中之一需要 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")
    });
};

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

【讨论】:

  • 我最终没有使用您的解决方案,而是最终从依赖于 cookie 值的代码中删除了 OutputCache
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-23
  • 1970-01-01
  • 2011-07-15
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
  • 2012-10-14
相关资源
最近更新 更多