1

问题:为什么从缓存中提取请求的 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");

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

4

2 回答 2

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

如何IHttpModule工作

IHttpModule接口如下所示:

public interface IHttpModule
{
    void Init(HttpApplication context);
    void Dispose();
}

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;

会话的工作方式取决于正在运行的会话模式,但要理解的关键是会话是在方法中检索和创建的,SessionStateModule.BeginAcquireState并且该方法与AcquireRequestState事件异步连接。

输出缓存的工作原理

System.WebIHttpModule定义了一个名为的内部实现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方法成功检索缓存的响应,它会调用实例上的CompleteRequest()方法。HttpApplication此方法的文档中写道:“导致 ASP.NET 绕过 HTTP 执行管道链中的所有事件和过滤,并直接执行 EndRequest 事件”。因此,在CompleteRequest()调用时间和EndRequest()事件之间发生的任何事件都将被跳过。

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

  • SessionStateModule订阅SessionStateModule.BeginAcquireState应用程序的事件AcquireRequestState,因此这是创建和检索会话的地方。
  • OutputCacheModule订阅OutputCacheModule.OnEnter应用程序的事件ResolveRequestCache,因此这是检索缓存响应和CompleteRequest()调用的位置。这意味着以下事件将永远不会为缓存的请求触发MapRequestHandler/PostMapRequestHandlerAcquireRequestState/PostAcquireRequestState; PreRequestHandlerExecute; IHttpHandler.ProcessRequest; PostRequestHandlerExecute; ReleaseRequestState/PostReleaseRequestState; UpdateRequestCache/PostUpdateRequestCache; 并且,LogRequest/PostLogRequest
  • 如果检索到缓存的响应,则HttpApplication.CompleteRequest()调用 then,该AcquireRequestState事件永远不会触发,并且永远不会创建会话。

该怎么办?

禁用页面上的输出缓存以支持控件是否是一个可行的解决方案取决于:(1)使用会话的代码是在控件中还是在页面中;(2) 你的应用实例需要能够支持多少负载;(3) 应用程序或其支持基础设施中存在哪些其他缓存机制。根据您的基础架构,可能还有其他因素需要考虑。例如,根据会话状态模式和环境是否负载平衡,ASP.NET 会话的执行和行为可能非常不同。此外,如果您碰巧正在运行像 Varnish 之类的 HTTP 加速器缓存(例如),在先前输出缓存的 ASP.NET 分页上启用会话可能会将行为从忽略会话更改为附加属于不同用户的陈旧会话.

于 2014-02-09T20:21:21.680 回答
0

显然这是默认的 .NET 行为。如果您创建一个新应用,启用会话,缓存一个 aspx 页面,然后让新用户拉取该页面的缓存版本,他们将不会获得会话/cookie。

作为一种解决方法...我认为我们将禁用页面输出缓存并使用更积极的控制缓存。

于 2013-11-09T18:28:20.620 回答