为了理解为什么输出缓存的请求不会创建会话,您需要了解一点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/PostMapRequestHandler:AcquireRequestState/PostAcquireRequestState; PreRequestHandlerExecute; IHttpHandler.ProcessRequest; PostRequestHandlerExecute; ReleaseRequestState/PostReleaseRequestState; UpdateRequestCache/PostUpdateRequestCache; 并且,LogRequest/PostLogRequest。
- 如果检索到缓存的响应,则
HttpApplication.CompleteRequest()调用 then,该AcquireRequestState事件永远不会触发,并且永远不会创建会话。
该怎么办?
禁用页面上的输出缓存以支持控件是否是一个可行的解决方案取决于:(1)使用会话的代码是在控件中还是在页面中;(2) 你的应用实例需要能够支持多少负载;(3) 应用程序或其支持基础设施中存在哪些其他缓存机制。根据您的基础架构,可能还有其他因素需要考虑。例如,根据会话状态模式和环境是否负载平衡,ASP.NET 会话的执行和行为可能非常不同。此外,如果您碰巧正在运行像 Varnish 之类的 HTTP 加速器缓存(例如),在先前输出缓存的 ASP.NET 分页上启用会话可能会将行为从忽略会话更改为附加属于不同用户的陈旧会话.