OutputCache VaryByCustom cookie值 [英] OutputCache VaryByCustom cookie value

查看:92
本文介绍了OutputCache VaryByCustom cookie值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有一种方法可以基于cookie值来设置OutputCache的值?

Is there a way to set the value of an OutputCache based on a cookie value?

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

For simplicities sake, this is my method

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

我的 Global.asax 具有此功能(顺序重写GetVaryByCustomString方法

My Global.asax has this (in order to override the GetVaryByCustomString method

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方法时,每次都遇到断点(这意味着缓存不起作用)。

I can verify that my browser has the ztest cookie, but when I debug the Index method, I hit the breakpoint every time (meaning that the cache isn't working).

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

The HttpResponse has no outbound cookies, so this point would not apply: https://msdn.microsoft.com/en-us/library/system.web.httpcookie.shareable(v=vs.110).aspx


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

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.


推荐答案

微妙的答案是否定的。

The subtle answer is no.

解释的答案如下:

输出缓存不能很好地使用的原因cookie

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

So the reason the output cache will not cache a response with cookies is that a cookie could be user-specific (e.g. authentication, analytical tracking, etc.). If one or more cookies with the property HttpCookie.Shareable = false, then the output cache considers the response uncacheable.

解决方案:

尽管有一些解决方法,但输出缓存将响应标头和内容缓存在一起,并且不提供任何修改钩子这些,然后再将它们发送回用户。但是,有一种方法可以提供在缓存的响应的标头发送回用户之前进行更改的功能。
其中之一需要 Fasterflect nuget包

There are some workarounds though, The output cache caches the response headers and content together and doesn't provide any hooks to modify these before sending them back to the user.However,there is a way to provide the ability to change before cached response's headers before they are sent back to the user. One of them requires the Fasterflect nuget package.

我有一个代码示例:

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

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

I don't know if it answers you question, but I hope it points to the right direction.

这篇关于OutputCache VaryByCustom cookie值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆