如果页面被缓存,为什么不创建会话?

Bra*_*d M 1 asp.net session outputcache iis-7.5

问题:当从缓存中提取请求的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");
Run Code Online (Sandbox Code Playgroud)

任何相关信息将不胜感激.

sma*_*man 7

为了理解输出缓存请求不会创建会话的原因,您需要了解ASP.NET应用程序生命周期(事件).基础是在每个请求期间可能触发的一系列定义的HttpApplication事件.这些事件通常由HttpModule实现或Global.asax文件使用事件处理程序订阅.

并非每个应用程序事件都会在每个请求中触发,但是触发的事件将始终以定义的顺序触发.在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
Run Code Online (Sandbox Code Playgroud)

如何IHttpModule工作

IHttpModule的接口看起来像:

public interface IHttpModule
{
    void Init(HttpApplication context);
    void Dispose();
}
Run Code Online (Sandbox Code Playgroud)

Init()方法用于将事件处理程序订阅到应用程序事件,并且该Dispose()方法在应用程序完成后清除模块.

ASP.NET会话的工作原理

System.Web定义一个IHttpModule名为的实现System.Web.SessionState.SessionStateModule.如果未在web.config中禁用会话,则会连接以下事件处理程序:

// app is the current HttpApplication ;
app.AddOnAcquireRequestStateAsync(this.BeginAcquireState, this.EndAcquireState);
app.ReleaseRequestState += this.OnReleaseState;
app.EndRequest          += this.OnEndRequest;
Run Code Online (Sandbox Code Playgroud)

根据正在运行的会话模式,会话的工作方式不同,但要理解的关键是在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

OnEnter方法评估缓存参数并查找与请求匹配的缓存响应.该OnLeave方法缓存可缓存的响应.您需要知道的一件事是,如果OnEnter方法成功检索到缓存的响应,它会调用实例上的CompleteRequest()方法HttpApplication.此方法的文档为:"使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分页上启用会话可能会更改从省略会话到附加属于其他用户的陈旧会话的行为.这只是一个假设的例子,但重点是在做出这些决定时可能还需要考虑其他因素.