有谁知道为什么我的页面上有cookie,输出缓存不起作用!
示例页面
<%@ 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>
背后的示例代码:
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
当部署到iis 6或7时,这不会缓存,但是如果我注释掉它的3个Response.Cookies行。
在VS中运行时,它可以正常工作。
在我设置response.cookies时,iis / web.config等中是否有一些设置允许outputcache。我知道cookie内容将被缓存,并且它只是缓存的http响应的一部分。
答案 0 :(得分:5)
在对这个问题进行了一些研究之后,我开始理解并解决这个问题。
输出缓存与Cookie无关的原因
因此输出缓存不会使用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(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>
可以像这样使用它来插入cookie:
HeaderModOutputCacheProvider.RequestServedFromCache += RequestServedFromCache;
HeaderModOutputCacheProvider.RequestServedFromCache += (sender, e) =>
{
e.AddCookies(new HttpCookieCollection
{
new HttpCookie("key", "value")
});
};
答案 1 :(得分:4)
您尝试在服务器端缓存此问题,同时尝试在客户端上设置Cookie - 这不能协同工作。
原因:当您在服务器端的缓存上设置页面时,在提供缓存版本(发送到客户端)时,不会运行代码。这是服务器上的缓存点。不要运行任何东西并从缓存中提供它。
也许你只需要在标题上设置缓存而不是在服务器上缓存整个页面。
答案 2 :(得分:3)
它是由不同版本的.NET框架引起的。基本上,某些版本永远不会缓存带有cookie集的页面。
答案 3 :(得分:1)
检查您是否正在运行.NET 2.0 SP1,以及是否已应用MS11-100(2012年12月发布)。
我们遇到了类似问题,最终联系了Microsoft支持部门。他们确认MS11-100打破了输出缓存,但声称它是设计的(由于补丁中修复的安全漏洞的性质),目前还没有做任何事情来恢复输出缓存功能。
一个简单的测试:如果您发现安装了补丁,只需卸载该补丁并重新启动即可。您应该看到输出缓存开始起作用。由于安全隐患,我认为没有人会建议将其作为生产解决方案,因此只能将其用作隔离问题的手段。
我们最终测试了一个更新的框架(你必须转到4.0; 3.5只是2.0框架的扩展而不是它自己的独立框架),并且在解决所有编译错误后,输出缓存立即开始工作。
我们还致力于改变我们与cookie交互的方式,以便我们可以继续使用2.0框架(毕竟,测试我们的cookie处理程序类应该更容易,而不是测试整个应用程序)。有很多障碍,最终的产品充斥着“黑客”,所以这是不可取的。
答案 4 :(得分:0)
我遇到了同样的问题,我通过设置Location =“ServerAndClient”测试了Aristos给出的场景,并且它有效。如果我只使用Location =“Server”,那么它就不起作用了。
答案 5 :(得分:-1)
有一种解决方法可能在某些情况下有效: 如果cookie不依赖于页面代码但可以使用某些独立代码计算,则可以在Application_EndRequest中设置cookie Application_EndRequest在OutputCache之后处理,因此缓存没有cookie存储,但是在将请求传递给客户端之前添加了set cookie头。