问题:当从缓存中提取请求的aspx页面时,为什么没有创建会话(和会话cookie)?
背景资料
我已经进行了不少谷歌搜索,但我找不到任何表明这是预期行为的内容。我们期望的行为是始终生成新的会话/ cookie,无论请求的页面是否从缓存中拉出。
我们正在使用以下代码缓存页面。 (.NET 3.5,IIS 7.5)
Response.Cache.SetExpires(DateTime.Now.AddMonths(1));
Response.Cache.SetCacheability(HttpCacheability.Server);
Response.Cache.SetVaryByCustom("IsLoggedIn");
Response.Cache.VaryByParams["*"] = true;
Response.Cache.SetValidUntilExpires(true);
Response.AddCacheItemDependency("Pages");
非常感谢任何相关信息。
答案 0 :(得分:7)
为了理解输出缓存请求为什么不创建会话,您需要了解ASP.NET Application Life Cycle事件。基础是在每个请求期间可能触发的定义的HttpApplication
events系列。这些事件通常由HttpModule实现或Global.asax file使用事件处理程序订阅。
并非每个应用程序事件都会在每个请求中触发,但触发的事件将始终按照定义的顺序触发。在IIS 7上触发的事件的顺序
1. BeginRequest
2. AuthenticateRequest
PostAuthenticateRequest
3. AuthorizeRequest
PostAuthorizeRequest
4. ResolveRequestCache
PostResolveRequestCache
5. MapRequestHandler (Integrated Mode Only)
PostMapRequestHandler
6. AcquireRequestState
PostAcquireRequestState
7. PreRequestHandlerExecute
<--- the IHttpHandler.ProcessRequest() method is called here
PostRequestHandlerExecute
8. ReleaseRequestState
PostReleaseRequestState
9. UpdateRequestCache
PostUpdateRequestCache
10. LogRequest (Integrated Mode Only)
PostLogRequest (Integrated Mode Only)
11. EndRequest
12. PreSendRequestHeaders
13. PreSendRequestContent
IHttpModule
如何运作IHttpModule界面如下所示:
public interface IHttpModule
{
void Init(HttpApplication context);
void Dispose();
}
Init()
方法用于将事件处理程序订阅到应用程序事件,并且Dispose()
方法在应用程序完成后清除模块。
System.Web
定义了名为System.Web.SessionState.SessionStateModule
的IHttpModule
实现。如果未在web.config中禁用会话,则会连接以下事件处理程序:
// app is the current HttpApplication ;
app.AddOnAcquireRequestStateAsync(this.BeginAcquireState, this.EndAcquireState);
app.ReleaseRequestState += this.OnReleaseState;
app.EndRequest += this.OnEndRequest;
会话的工作方式不同,具体取决于正在运行的会话模式,但要理解的关键是在SessionStateModule.BeginAcquireState
方法中检索和创建会话,并且该方法与AcquireRequestState
事件异步连接。
System.Web
定义了一个名为IHttpModule
的内部System.Web.Caching.OutputCacheModule
实现。 Init()
方法如下:
void IHttpModule.Init(HttpApplication app)
{
if (RuntimeConfig.GetAppConfig().OutputCache.EnableOutputCache)
{
app.ResolveRequestCache += new EventHandler(this.OnEnter);
app.UpdateRequestCache += new EventHandler(this.OnLeave);
}
}
OnEnter
方法评估缓存参数并查找与请求匹配的缓存响应。 OnLeave
方法缓存可缓存的响应。您需要知道的一件事是,如果OnEnter
方法成功检索到缓存的响应,它会调用HttpApplication
实例上的CompleteRequest()方法。此方法的文档为:“使ASP.NET绕过所有事件并在HTTP管道执行链中进行过滤,并直接执行EndRequest事件”。因此,跳过在CompleteRequest()
被调用的时间和EndRequest()
事件之间发生的任何事件。
SessionStateModule
订阅SessionStateModule.BeginAcquireState
到应用程序的AcquireRequestState
事件,因此这是创建和检索会话的地方。OutputCacheModule
为应用程序的OutputCacheModule.OnEnter
事件订阅ResolveRequestCache
,这样就可以检索缓存的响应以及调用CompleteRequest()
的位置。这意味着以下事件永远不会为缓存的请求触发:MapRequestHandler/PostMapRequestHandler
; AcquireRequestState/PostAcquireRequestState
; PreRequestHandlerExecute
; IHttpHandler.ProcessRequest
; PostRequestHandlerExecute
; ReleaseRequestState/PostReleaseRequestState
; UpdateRequestCache/PostUpdateRequestCache
;和LogRequest/PostLogRequest
。HttpApplication.CompleteRequest()
,AcquireRequestState
事件永远不会触发,并且永远不会创建会话。是否在页面上禁用输出缓存以支持控件是一种可行的解决方案取决于:(1)使用会话的代码是在控件中还是在页面中; (2)您的应用程序实例需要支持多少负载; (3)应用程序或其支持基础结构中存在哪些其他缓存机制。根据您的基础设施,可能还有其他因素需要考虑。例如,ASP.NET会话的执行和行为可能会有很大不同,具体取决于会话状态模式以及环境是否负载平衡。此外,如果您正在运行像Varnish这样的HTTP加速器缓存(例如),则在先前输出缓存的ASP.NET分页上启用会话可能会更改从省略会话到附加属于其他用户的陈旧会话的行为。这只是一个假设的例子,但重点是在做出这些决定时可能还需要考虑其他因素。
答案 1 :(得分:0)
显然这是默认的.NET行为。如果您创建一个新的应用程序,启用会话,缓存一个aspx页面,然后让一个新用户拉出该页面的缓存版本,他们将没有会话/ cookie。
作为一种解决方法......我认为我们只会禁用页面输出缓存并使用更积极的控制缓存。