c# – OutputCache VaryByCustom cookie值

有没有办法根据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

If a given HttpResponse contains one or more outbound cookies with Shareable is set to false (the default value),output caching will be suppressed for the response. This prevents cookies that contain potentially sensitive information from being cached in the response and sent to multiple clients. To allow a response containing cookies to be cached,configure caching normally for the response,such as using the OutputCache directive or MVC’s [OutputCache] attribute,and set all outbound cookies to have Shareable set to true.

解决方法

微妙的答案是否定的.

解释的答案如下:

输出缓存与cookie不兼容的原因

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

解:

但是有一些解决方法,输出缓存将响应头和内容缓存在一起,并且在将它们发送回用户之前不提供任何钩子来修改它们.但是,有一种方法可以在缓存响应的头之前提供更改在它们被发回给用户之前.
其中一个需要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(int),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,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",toClone.GetFieldValue("_kernelCacheUrl",toClone.GetFieldValue("_dependenciesKey",toClone.GetFieldValue("_dependencies",toClone.GetFieldValue("_statusCode",toClone.GetFieldValue("_statusDescription",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")
    });
};

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

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...