88

I could really do with updating a user's session variables from within my HTTPModule, but from what I can see, it isn't possible.

UPDATE: My code is currently running inside the OnBeginRequest () event handler.

UPDATE: Following advice received so far, I tried adding this to the Init () routine in my HTTPModule:

AddHandler context.PreRequestHandlerExecute, AddressOf OnPreRequestHandlerExecute

But in my OnPreRequestHandlerExecute routine, the session state is still unavailable!

Thanks, and apologies if I'm missing something!

Chris Roberts
  • 18,622
  • 12
  • 60
  • 67

6 Answers6

83

Found this over on the ASP.NET forums:

using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;

// This code demonstrates how to make session state available in HttpModule,
// regardless of requested resource.
// author: Tomasz Jastrzebski

public class MyHttpModule : IHttpModule
{
   public void Init(HttpApplication application)
   {
      application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
      application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
   }

   void Application_PostMapRequestHandler(object source, EventArgs e)
   {
      HttpApplication app = (HttpApplication)source;

      if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
         // no need to replace the current handler
         return;
      }

      // swap the current handler
      app.Context.Handler = new MyHttpHandler(app.Context.Handler);
   }

   void Application_PostAcquireRequestState(object source, EventArgs e)
   {
      HttpApplication app = (HttpApplication)source;

      MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;

      if (resourceHttpHandler != null) {
         // set the original handler back
         HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
      }

      // -> at this point session state should be available

      Debug.Assert(app.Session != null, "it did not work :(");
   }

   public void Dispose()
   {

   }

   // a temp handler used to force the SessionStateModule to load session state
   public class MyHttpHandler : IHttpHandler, IRequiresSessionState
   {
      internal readonly IHttpHandler OriginalHandler;

      public MyHttpHandler(IHttpHandler originalHandler)
      {
         OriginalHandler = originalHandler;
      }

      public void ProcessRequest(HttpContext context)
      {
         // do not worry, ProcessRequest() will not be called, but let's be safe
         throw new InvalidOperationException("MyHttpHandler cannot process requests.");
      }

      public bool IsReusable
      {
         // IsReusable must be set to false since class has a member!
         get { return false; }
      }
   }
}
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Jim Harte
  • 2,593
  • 3
  • 23
  • 20
  • 8
    MS should to fix this!... if i mark a Module as implementing the IRequiresSessionState, i shouldn't have to jump thru a hoop to get it... ( sexy code indeed ) – BigBlondeViking Jul 28 '09 at 20:26
  • 6
    Nice code. I thought I would need this, but it turns out not. This code ends up loading the session for every image and other non-page resource that goes through the server. In my case, I simply check if session is null in the PostAcquireRequestState event and return if it is. – Abtin Forouzandeh Feb 24 '10 at 23:27
  • 7
    This code is useful if the resource requested doesn't handle session state. For standard .aspx pages simply add your code accessing the session on the PostAcquireRequestState event handler. Session state won't be available on any BeginRequest event handler because the session state has not been acquired yet. – JCallico Jun 18 '10 at 19:32
  • Fantastic. Thx amigo, this code really did the trick to use HttpContext.Current.User within an HttpModule – user29964 Jun 21 '10 at 13:45
  • 1
    For those wanting to use this "hack", the only way I could get this work is to make the temp handler use IReadOnlySessionState instead of IRequiresSessionState – nokturnal Jan 16 '13 at 20:05
  • 3
    It doesn't work in my case. I got "Session state is not available in this context." when there is a request trying to access a static file. Any help ? – maxisam Jul 22 '13 at 18:05
  • `OriginalHandler.ProcessRequest(context);` in `ProcessRequest` if your code **actually does** call `ProcessRequest()` – TheGeekZn Aug 08 '14 at 09:16
  • 3
    For this to work on static files, I have, in addition, re-register the session module (in the web.config) by removing the preCondition="managedHandler" () – nlips Dec 04 '14 at 12:45
  • This worked well to secure a folder of pdf files, using my existing login/session code. I found a similar implementation that splits it into 3 sections, doing the session checking in PreRequestHandlerExecute. I found this clearer to understand. http://www.martinwilley.com/net/code/AuthorizeModule.html. I went with IReadOnlySessionState as nokturnal just not to mess around, and also needed the web.config change from @nlips – goodeye Dec 13 '14 at 18:10
  • 1
    using [`SetSessionStateBehavior`](https://msdn.microsoft.com/en-us/library/system.web.httpcontext.setsessionstatebehavior%28v=vs.110%29.aspx) is much more straightforward than implementing the workaround in this answer. – zzzzBov Mar 04 '15 at 22:35
38

HttpContext.Current.Session should Just Work, assuming your HTTP Module isn't handling any pipeline events that occur prior to the session state being initialized...

EDIT, after clarification in comments: when handling the BeginRequest event, the Session object will indeed still be null/Nothing, as it hasn't been initialized by the ASP.NET runtime yet. To work around this, move your handling code to an event that occurs after PostAcquireRequestState -- I like PreRequestHandlerExecute for that myself, as all low-level work is pretty much done at this stage, but you still pre-empt any normal processing.

mdb
  • 52,000
  • 11
  • 64
  • 62
  • Unfortunately that's not available in the HTTPModule - "Object reference not set to an instance of an object." – Chris Roberts Nov 09 '08 at 19:39
  • I'm processing 'OnBeginRequest'? – Chris Roberts Nov 09 '08 at 19:39
  • Thanks for the update. If I handle it in an Application level event, why don't I just do all my processing at application level instead of using an HTTPModule? – Chris Roberts Nov 09 '08 at 19:47
  • 1
    PostAcquireRequeststate isn't an 'application level event': if the HTTP request is handled by a web service handler, for example, you'll still see it in your HTTP module, but not in Global.asax... – mdb Nov 09 '08 at 19:50
  • 1
    This doesn't seem to work reliably for me. The following code will often cause an exception 'Session state is not available in this context'. In fact, it crashes the VS debugger quite spectacularly. context.PreRequestHandlerExecute += (sender, args) => Console.Write(((HttpApplication) sender).Session["test"]; – cbp May 24 '11 at 07:19
  • I'm trying this out and everything seems to work fine except GET requests - they don't make it past the ResolveRequestCache event now. Previously, I had everything in AuthenticateRequest (which is prior to ResolveRequestCache) but now I've moved my code into PreRequestHandlerExecute so that I can use Session State. First it appeared that my HTTP Module wasn't even seeing the GET requests, but by subscribing to each event in the HTTP Module life cycle and logging each one, I can see that my GET requests do come through but stop after the ResolveRequestCache event. Any thoughts? – lhan Jan 21 '13 at 21:20
  • I should also note that Fiddler shows a 404-Not found for my GET requests. Is there a setting in IIS I need to change to make this solution work with GET? – lhan Jan 21 '13 at 21:21
15

Accessing the HttpContext.Current.Session in a IHttpModule can be done in the PreRequestHandlerExecute handler.

PreRequestHandlerExecute: "Occurs just before ASP.NET starts executing an event handler (for example, a page or an XML Web service)." This means that before an 'aspx' page is served this event gets executed. The 'session state' is available so you can knock yourself out.

Example:

public class SessionModule : IHttpModule 
    {
        public void Init(HttpApplication context)
        {
            context.BeginRequest += BeginTransaction;
            context.EndRequest += CommitAndCloseSession;
            context.PreRequestHandlerExecute += PreRequestHandlerExecute;
        }



        public void Dispose() { }

        public void PreRequestHandlerExecute(object sender, EventArgs e)
        {
            var context = ((HttpApplication)sender).Context;
            context.Session["some_sesion"] = new SomeObject();
        }
...
}
Bert Persyn
  • 401
  • 4
  • 13
  • I tried this, and you get the Session indeed. But it seems like the RequestHeader is not fully there, especally the HeaderContentType – Matthias Müller May 19 '14 at 09:50
12

If you're writing a normal, basic HttpModule in a managed application that you want to apply to asp.net requests through pages or handlers, you just have to make sure you're using an event in the lifecycle after session creation. PreRequestHandlerExecute instead of Begin_Request is usually where I go. mdb has it right in his edit.

The longer code snippet originally listed as answering the question works, but is complicated and broader than the initial question. It will handle the case when the content is coming from something that doesn't have an ASP.net handler available where you can implement the IRequiresSessionState interface, thus triggering the session mechanism to make it available. (Like a static gif file on disk). It's basically setting a dummy handler that then just implements that interface to make the session available.

If you just want the session for your code, just pick the right event to handle in your module.

Rob
  • 1,364
  • 11
  • 17
2

Since .NET 4.0 there is no need for this hack with IHttpHandler to load Session state (like one in most upvoted answer). There is a method HttpContext.SetSessionStateBehavior to define needed session behaviour. If Session is needed on all requests set runAllManagedModulesForAllRequests to true in web.config HttpModule declaration, but be aware that there is a significant performance cost running all modules for all requests, so be sure to use preCondition="managedHandler" if you don't need Session for all requests. For future readers here is a complete example:

web.config declaration - invoking HttpModule for all requests:

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        <add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess"/>
    </modules>
</system.webServer>

web.config declaration - invoking HttpModule only for managed requests:

<system.webServer>
    <modules>
        <add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess" preCondition="managedHandler"/>
    </modules>
</system.webServer>

IHttpModule implementation:

namespace HttpModuleWithSessionAccess
{
    public class ModuleWithSessionAccess : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.BeginRequest += Context_BeginRequest;
            context.PreRequestHandlerExecute += Context_PreRequestHandlerExecute;
        }

        private void Context_BeginRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
        }
    
        private void Context_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            if (app.Context.Session != null)
            {
                app.Context.Session["Random"] = $"Random value: {new Random().Next()}";
            }
        }

        public void Dispose()
        {
        }
    }
}
Antonio Bakula
  • 20,445
  • 6
  • 75
  • 102
  • 1
    My hero! Spent 4 hours on this using different methods and got null Session state. I was missing `Context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);` – nothingisnecessary Aug 22 '22 at 15:32
0

Try it: in class MyHttpModule declare:

private HttpApplication contextapp;

Then:

public void Init(HttpApplication application)
{
     //Must be after AcquireRequestState - the session exist after RequestState
     application.PostAcquireRequestState += new EventHandler(MyNewEvent);
     this.contextapp=application;
}  

And so, in another method (the event) in the same class:

public void MyNewEvent(object sender, EventArgs e)
{
    //A example...
    if(contextoapp.Context.Session != null)
    {
       this.contextapp.Context.Session.Timeout=30;
       System.Diagnostics.Debug.WriteLine("Timeout changed");
    }
}
aTest
  • 1