Asp.net outputcache and cookies

Does anyone know why if my page has cookies, the output cache does not work!

Page example

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="ct.aspx.vb" Inherits="ct" %> <%@ OutputCache Duration="600" Location="Server" VaryByParam="none" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <h1>Cache test</h1> <p id="rndout" runat="server"></p> </div> </form> </body> </html> 

Sample code for

 Partial Class ct Inherits System.Web.UI.Page Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load Dim rc As New Random() Dim rn As Integer rn = rc.Next() rndout.InnerHtml = rn.ToString Response.Cookies("sym")("hello") = "world" Response.Cookies("sym").Expires = DateTime.Now.AddDays(370) Response.Cookies("sym").Domain = Application.Get("cookieurl") End Sub End Class 

when deploying to iis 6 or 7, this does not cache, however, if I comment on lines 3 of Response.Cookies, it does.

When launched in VS, it works great in both directions.

Is there any setting in iis / web.config etc. to allow the output file when I set response.cookies. I understand that the contents of the cookie will be cached, and is also part of the HTTP response that is cached.

thanks

Simeon.

+6
cookies outputcache
source share
6 answers

You are trying to cache this on the server side and at the same time trying to set a cookie on the client - this does not work together.

Why: When you set the page in the cache on the server side, the code does not run when the cached version is served (send to the client). This is the cache point on the server. In order not to run anything and not to give it out of the cache, as it is.

Perhaps you just need to set the cache on the header and not cache the full page on the server.

+4
source share

After a little research on this problem, I came to understand and solve this problem.

The reason that the output cache does not play good results with cookies

Therefore, the reason the output cache will not cache the response with cookies is because the cookie may be user-specific (e.g. authentication, analytic tracking, etc.). If one or more cookies with the property HttpCookie.Shareable = false , then the output cache considers the answer incompatible.

Including cookies with cached response

That's where it gets complicated. The output cache caches the headers and contents of the responses together and does not provide any hooks to change them before sending them back to the user. However, I wrote the following custom output cache provider to provide the ability to change the cached response headers before they are sent back to the user (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"))); } } } } 

You would connect it like this:

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

And you can use it to insert cookies:

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

This is caused by different versions of the .NET framework. Basically, some versions will never cache a cookie page.

See the publication of this blog .

+2
source share

Make sure you are using .NET 2.0 SP1, and if you have applied MS11-100 (released December 2012).

We encountered similar problems and turned to Microsoft support. They confirmed that MS11-100 interrupted output caching, but claimed that it was developed (due to the nature of the security vulnerabilities fixed in the patch), and currently nothing is being done to restore the functionality of the output cache.

A simple test: if you find that you have a patch installed, simply uninstall that patch and reboot. You should see that output caching is starting to work. I donโ€™t think anyone would recommend this as a production solution because of the security implications, so use this only as a means to isolate the problem.

We finished testing the new structure (you need to switch to version 4.0; 3.5 is just an extension of the 2.0 environment, not a separate infrastructure) and after fixing all the compilation errors, output caching immediately started working.

We also worked on how we interact with cookies so that we can stay on platform 2.0 (in the end, it should be easier to test our cookie handler classes instead of testing the entire application). There are a number of obstacles, and the final product smells like โ€œhacksโ€, so it wasnโ€™t good.

+1
source share

I had the same problem and I tested the script Aristos set by setting Location = "ServerAndClient" and it works. If I use Location = "Server" then this did not work.

0
source share

There is a workaround that may work in some scenarios: If the cookie is not very dependent on the page code, but can be calculated using some autonomous code, you can set the cookie in Application_EndRequest Application_EndRequest is processed after OutputCache and therefore the cache is stored without cookie, but then the header of the set cookie is added before the request is delivered to the client.

-one
source share

All Articles