5

I'm trying to get Session enabled in the GettHttpHandler method of my IRouteHandler classes but session is always null. Could someone tell me what I'm doing wrong?

In global.asax I have

RouteTable.Routes.Add("All", new Route("{*page}", new MyRouteHandler()));

The MyRouteHandler class where Session is null looks like this:

public class MyRouteHandler : IRouteHandler, IRequiresSessionState
{
    public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        string test = HttpContext.Current.Session["test"].ToString();
        return BuildManager.CreateInstanceFromVirtualPath("~/Page.aspx", typeof(Page)) as Page;
    }
}

I made a small test app that shows the problem.

Could someone tell me what I'm doing wrong?

Edited to add:

Yes, I really need session data in the route handler. There are many reasons but one easily explainable is when the user can switch to browse the site in preview mode.

The site consists of a hierarchy of dynamic pages (/page1/page2...) in the database that can be published normally or to preview. A content producer browsing the site can choose to view just normal pages or also those published to preview. The browsing mode is stored in the user's session so therefor the route handler needs to know the browsing mode to be able to solve the requested page.

So I really need the session already at that stage.

Dori
  • 915
  • 1
  • 12
  • 20
Mathias Rönnlund
  • 4,078
  • 7
  • 43
  • 96
  • I seems that Session is loaded later on in the page life cycle and therefor isn't available when the IRouteHandler.GetHttpHandler launches. I had to do some workarounds for my scenario to work but unfortunately never were able to access the Session until in an aspx-page (or other IHttpHandler). – Mathias Rönnlund Jan 03 '11 at 08:21
  • Did you ever figure this out? I have tried all of the possible solutions Clear Modules and handlers, runAllManagedModulesForAllRequests, added .aspx to the end. No matter what when using system.web.routing Session Start does not fire. –  Dec 31 '10 at 21:08

3 Answers3

8

I have explained reason behind this problem in this answer. And now I have found a solution to the problem!

  1. You create a custom HttpHandler class:

    class MyHttpHandler : IHttpHandler, IRequiresSessionState
    {
      public MyRequestHandler RequestHandler;
      public RequestContext Context;
      public MyHttpHandler(MyRequestHandler routeHandler, RequestContext context)
      {
        RequestHandler = routeHandler;
        Context = context;
      }
    
      public void ProcessRequest(HttpContext context)
      {
        throw new NotImplementedException();
      }
    
      public bool IsReusable
      {
        get { throw new NotImplementedException(); }
      }
    }
    

It is important to add IRequiresSessionState interface, otherwise IIS does not load session for this request. We do not need to implement logic of ProcessRequest and IsReusable, but class must implement the IHttpHandler interface.

  1. You change your RouteHandler implementation:

    public class MyRequestHandler : IRouteHandler
    {
      public IHttpHandler GetHttpHandler(RequestContext requestContext)
      {
          return new MyHttpHandler(this, requestContext);
      }
    
      public IHttpHandler DelayedGetHttpHandler(RequestContext requestContext)
      {
          // your custom routing logic comes here...
      }
    }
    

You simply move your original, Session dependent logic to DelayedGetHttpHandler function and in the GetHttphandler function you return an instance of the helping MyHttpHandler class.

  1. Then, you hook your handling logic to the HttpApplication.PostAcquireRequestState event, e.g. in the Global.asax:

    public class Global : HttpApplication
    {
        public override void Init()
        {
            base.Init();
            PostAcquireRequestState += Global_PostAcquireRequestState;
        }
     }
    

For more reference, check this page: https://msdn.microsoft.com/en-us/library/bb470252(v=vs.140).aspx. It explains the request lifecycle and why I use the PostAcquireRequestState event.

  1. In the event handler, you invoke your custom RouteHandling function:

    void Global_PostAcquireRequestState(object sender, EventArgs e)
    {
       if (HttpContext.Current.Handler is MyHttpHandler) {
         var handler = HttpContext.Current.Handler as MyHttpHandler;
         HttpContext.Current.Handler = handler.RouteHandler.DelayedGetHttpHandler(handler.Context);
       }
    }
    

And that's it. Works for me.

petrosmm
  • 528
  • 9
  • 23
Jan Kukacka
  • 1,196
  • 1
  • 14
  • 29
  • THIS was the solution I have spent 8 hours looking for. I'm using MVC 5 and all the options for adding modules to web.config and having custom controllerhandlers didn't work. This did. However why not just put your logic in ProcessRequest and not bother with any of the global.asax - stop at point 2 and don't bother with the delayedhandler. – Tod May 20 '15 at 10:05
  • Ohh and "RouteHandler" in MyHttpHander should be "RequestHandler" – Tod May 20 '15 at 10:08
  • Thanks for that! Great solution! Now I can serve different pages for the same URL depending on what user is logged in ^_^. – Alex Jul 15 '15 at 21:57
  • In my case, the MyHttpHandler method and property were invoked in some situations. I would remove the NotImplementedExceptions to be sure. – Jorrit Schippers Nov 30 '15 at 09:17
  • This worked for me, except that step 1 and 2 are not necessary. You simply can subscribe to the PostAcquireRequestState within the Init() method in Global.asax (step 3) and do all the route manipulation directly to the HttpContext.Current.Handler in the eventhandler (step 4) w/o having to create a custom HttpHandler or RouteHandler. – Manu Mar 14 '16 at 08:49
  • I need the session to be availible in webapi inside of areas..... Similar in concept to this, however, I followed: https://stackoverflow.com/a/25371394/399752, and (to override IRouteHandler) which is very similar to the example found here.... https://stackoverflow.com/a/11479021/3997521 then inside `public static Route MapHttpRoute(this AreaRegistrationContext context, string name, string routeTemplate, object defaults, object constraints)` I added `route.RouteHandler = new MyHttpControllerHandler();` – petrosmm Sep 27 '18 at 19:11
1

I am not sure that you can do this (although I may be wrong). My recollection is that IRequiresSessionState indicates that an HttpHandler needs session state, rather than the route handler (which is responsible for giving you a handler appropriate to the route).

Do you really need the session in the route handler itself and not the handler it gives out?

David M
  • 71,481
  • 13
  • 158
  • 186
Rob Levine
  • 40,328
  • 13
  • 85
  • 111
1

Well I know this is old thread but just putting up the answer here if anyone like me falls in the same scenario, I found an answer here

What you do is just add a runAllManagedModulesForAllRequests="true" attribute to your modules tag in web.config like below

    <system.webServer>
    .....
       <modules runAllManagedModulesForAllRequests="true">
       ........
       </modules>
    ......
    </system.webServer>

However this is not a good solution as it calls managed module everytime, i am using

    <remove name="Session" />
    <add name="Session" type="System.Web.SessionState.SessionStateModule"/>

add it in modules section of web.config, this a better solution than the previous one.

Community
  • 1
  • 1
DotNetUser
  • 6,494
  • 1
  • 25
  • 27
  • This is not working. `HttpContext.Current.Session` is still `null`. Moreover, if something is bad code or not a solution (as in your solution #1 that didn't work), you should definitely remove it from your answer or move it to the bottom and say something like "what doesn't work" before you show it. – toddmo Feb 11 '17 at 19:59