Вопрос

Question: Why is there no session created (and session cookie) when an aspx page requested is pulled from cache?

Background Information

I've made quite a few google searches, but I can't find anything that indicates that this is the intended behavior. Our desired behavior is that a new session/cookie are always generated regardless of whether the page requested is pulled from cache.

We are caching pages using the following code. (.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");

Any relevant information would be greatly appreciated.

Это было полезно?

Решение

In order to understand why an output-cached request will not create a session, you need to understand a little bit about the ASP.NET Application Life Cycle, the events . The basics are that are a defined series of HttpApplication events that may fire during each request. These events are typically subscribed to using event handlers by either HttpModule implementations or a Global.asax file.

Not every application event fires on every request, but the events that do fire will always fire in the defined order. The order of events that fire on 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

How an IHttpModule Works

The IHttpModule interface looks like:

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

The Init() method is used to subscribes event handlers to the application events and the Dispose() method cleans up after the module when the application is done with it.

How ASP.NET Sessions Work

System.Web defines an IHttpModule implementation named System.Web.SessionState.SessionStateModule. If the session is not disabled in the web.config, the following event handlers get wired up:

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

Sessions work differently depending on what session mode is running but the key thing to understand is that sessions are retrieved and created in the SessionStateModule.BeginAcquireState method and that method is wired up asynchronously to the AcquireRequestState event.

How Output Caching Works

System.Web defines an internal IHttpModule implementation named System.Web.Caching.OutputCacheModule. The Init() method looks like:

void IHttpModule.Init(HttpApplication app)
{
    if (RuntimeConfig.GetAppConfig().OutputCache.EnableOutputCache)
    {
        app.ResolveRequestCache += new EventHandler(this.OnEnter);
        app.UpdateRequestCache  += new EventHandler(this.OnLeave);
    }
}

The OnEnter method evaluates the cache parameters and looks for a cached response that matches the request. The OnLeave method caches cacheable responses. The one thing you need to know is that if the OnEnter method is successful in retrieving a cached response, it calls the CompleteRequest() method on the HttpApplication instance. The documentation for this method reads: "Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event". So, any events that occur between the time that CompleteRequest() is called and the EndRequest() event are skipped.

Why is session not created if page is cached?

  • The SessionStateModule subscribes SessionStateModule.BeginAcquireState to the application's AcquireRequestState event, so that is where sessions are created and retrieved.
  • The OutputCacheModule subscribes OutputCacheModule.OnEnter to the application's ResolveRequestCache event, so that is where cached responses are retrieved and where CompleteRequest() gets called. That means that the following events will never fire for a cached request: MapRequestHandler/PostMapRequestHandler; AcquireRequestState/PostAcquireRequestState; PreRequestHandlerExecute; IHttpHandler.ProcessRequest; PostRequestHandlerExecute; ReleaseRequestState/PostReleaseRequestState; UpdateRequestCache/PostUpdateRequestCache; and, LogRequest/PostLogRequest.
  • If a cached response is retrieved, then HttpApplication.CompleteRequest() is called, the AcquireRequestState event never fires, and the session is never created.

What to do about it?

Whether disabling output cache on pages in favor of controls is a viable solution depends on: (1) whether the code that uses the session is in the controls or in the pages; (2) how much load your application instance needs to be able to support; and, (3) what other caching mechanisms exist in either the application or its supporting infrastructure. Depending on your infrastructure, there may be other factors to consider. For example, ASP.NET sessions can perform and behave very differently depending on the session state mode and on whether the environment is load balanced or not. Also, if you happened to be running an HTTP accelerator cache like Varnish (for example), enabling sessions on a previously output-cached ASP.NET paged might change the behavior from omitting a session to attaching a stale session that belongs to a different user. This is just a hypothetical example, but the point is that there may be additional factors to consider when making these kinds of decisions.

Другие советы

Apparently this is default .NET behavior. If you create a new app, enable session, cache an aspx page, and then have a new user pull the cached version of that page, they will be given no session/cookie.

As a workaround...I think we'll just disable page outputcache and use more aggressive control caching.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top