locked
MVC 2 RC: ASP.NET Session timeout handling in Global.asax RRS feed

  • Question

  • User-578366845 posted

    I am able to intercept MVC 2 RC ASP.NET Session timeouts in the Global.asax.cs:

    protected void Session_Start()
    {
       if (Context.Session != null)
      {
           if (Context.Session.IsNewSession)
          {
                string sCookieHeader = Request.Headers["Cookie"];
                if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
               {
                  // how to simulate it ???
                  // RedirectToAction(“ActionName”, “ControllerName”,  route values);
               }
    
           }
        }
    
    }
    


     

    The question is how to redirect program flow to a particular controller action. Basically, I would like to simulate MVC’s RedirectToAction(“ActionName”, “ControllerName”,  route values) call inside Global.asax.cs.

    I want to re-route the current request to another controller/action, while keeping the execution path exactly the same as if that controller/action - inside global.asax - was requested.

    Regards,
    Yitzhak

     

    Thursday, January 28, 2010 3:29 PM

Answers

  • User1679353836 posted

    Hi:

    I have been working on this issue with Yitzhak and he came up the the following solution:

    protected void Session_Start()
            {
                if (Context.Session != null)
                {
                    if (Context.Session.IsNewSession)
                    {
                        string sCookieHeader = Request.Headers["Cookie"];
                        if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                        {
                            // intercept current route
                            HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current);
                            RouteData routeData = RouteTable.Routes.GetRouteData(currentContext);
                          
                            var domainRoute = routeData.Route;
    
                            // substitute route values
                            routeData.Values["controller"] = "homemain";
                            routeData.Values["action"] = "timeout";
    
                            // Clear the error on server.
                            Server.ClearError();
                            Response.Clear();
    
                            // Call target Controller and pass the routeData.
                            IController myController = new Main.Controllers.HomeMainController();
                            myController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    
                            Response.Flush();
                            Response.End();
                        }
                    }
                }
            }

     

    We are puzzled on 2 items:

    1. With the below lines commented:


    // Response.Flush();
    // Response.End();

    the previous page is remembered and displayed as well as the time out page.  All on the same page.  Why is the previous page still being remembered when the time out page is the one being called from the controller 

    2.  With both lines uncommented: 

     Response.Flush();
     Response.End();

    the timeout page is properly displayed but in the address bar it shows the previous pages url.  How do we get the address bar to show the proper url? 

    We have tried Response.Redirect("/controllerName/action/value"); and it is not working. It goes into an infinite loop by constantly creating a new session state.  

    Please advise.


     

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, February 1, 2010 1:26 PM
  • User-578366845 posted

    Below is our final working implementation.

     protected void Session_Start()
    {
         if (Context.Session != null)
        {
               if (Context.Session.IsNewSession)
              {
                  string sCookieHeader = Request.Headers["Cookie"];
                  if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                  {
                    //intercept current route
                    HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current);
                    RouteData routeData = RouteTable.Routes.GetRouteData(currentContext);
                            
                    //Substitute route Data Token Values for the Area
                    routeData.DataTokens["area"] = "";
                    routeData.DataTokens["UseNamespaceFallback"] = true;
    
                    //substitute route values
                    routeData.Values["controller"] = "homemain";
                    routeData.Values["action"] = "timeout";
    
                    IRouteHandler routeHandler = routeData.RouteHandler;
                    RequestContext requestContext = new RequestContext(currentContext, routeData);
    
                    IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
                    httpHandler.ProcessRequest(Context);
    
                    Response.Flush();
                    Response.End();
                   }
               }
            }
        }
    }
    


    But the original questions are still not answered:

    1. Why after calling GetRouteData() in the current context and replacing both routing controller and action,  MVC still goes to the already replaced/original controller and action?
      To prevent this a call to Response.End() is needed.
      What could be done to prevent such phenomena?
    2. How to affect the browser address URL?
      It still shows up the replaced/original controller and action.

     

    Microsoft ASP.NET MVC team members: bradwils, joecar, levib, Eilon, jeloff, and also ricka6, KeFang Chen, please shed some light on the subject.

     

    Regards,
    Yitzhak

     

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, February 3, 2010 1:44 PM

All replies

  • User197322208 posted

    You can try :
    Context.Current.RewritePath(your url destination)


    Thursday, January 28, 2010 3:52 PM
  • User-578366845 posted

    Hi ignatandrei,

    I tried and the verdict is that the proposed solution is not working.

    Beyond that this is MVC Web app. So I need to call an action method on a controller.

    Regards,
    Yitzhak

     

    Thursday, January 28, 2010 4:17 PM
  • User197322208 posted

    I tried and the verdict is that the proposed solution is not working.

    Sorry

    Try with Response.Redirect("~/controller/action/value")

    Beyond that this is MVC Web app. So I need to call an action method on a controller.

    MVC is , as WebForms, build over ASP.NET  .


    Thursday, January 28, 2010 4:49 PM
  • User940174373 posted

    Another option that is different is to keep your session alive via a heartbeat.  That way your session doesn't time out.

    http://www.dotnetcurry.com/ShowArticle.aspx?ID=453

    Thursday, January 28, 2010 7:15 PM
  • User-578366845 posted

    Response.Redirect("~/controller/action/value") idea is not working either.

    I am getting into the infinite  loop. The app. is calling the Session_Start() method again and again as a new session.

    Regards,
    Yitzhak

    Thursday, January 28, 2010 8:23 PM
  • User-578366845 posted

    Looking forward for the MVC community input on the proposed solution along the following lines:

    RouteData routeData = new RouteData(); 
    
    routeData.Values.Add("controller", "HomeMain"); 
    routeData.Values.Add("action", "SessionTimeout"); 
    
    // Clear the error on server. 
    Server.ClearError();
    Response.Clear();
    
    // Call target Controller and pass the routeData. 
    IController HomeMainController = new HomeMainController(); 
    HomeMainController.Execute(new RequestContext(     
           new HttpContextWrapper(Context), routeData)); 

     

    Regards,
    Yitzhak

     

    Thursday, January 28, 2010 10:03 PM
  • User197322208 posted

    Response.Redirect("~/controller/action/value") idea is not working either.

    I am getting into the infinite  loop. The app. is calling the Session_Start() method again and again as a new session.

    It works on my test without getting into the infinite  loop ....


    Friday, January 29, 2010 3:07 AM
  • User-578366845 posted

    Hi ignatandrei

    Response.Redirect("~/controller/action/value") is still a puzzle.

    On my side the

    Response.Redirect("~/HomeMain/SessionTimeout");

    still gets into the infinite loop.

    My environment has the following:

    • Windows 7 Enterprise 32-bit
    • ASP.NET MVC 2 RC
    • VS2008 SP1
    • VS solution has a single project with multi-area structure
    • I am executing my project staying inside in the VS2008
    • If I manually type in the IE address bar http://localhost:65316/Homemain/SessionTimeout, it navigates there without any problem.

    Please compare your environment against mine.

    Regards,
    Yitzhak

    Friday, January 29, 2010 9:40 AM
  • User197322208 posted

    Before, please remove the space betweeen HomeMain and /SessionTimeout  in

    Response.Redirect("~/HomeMain /SessionTimeout")

    Thank you

    Friday, January 29, 2010 9:57 AM
  • User-578366845 posted

    Hi ignatandrei,

    There is no space there. It is just fat fingers.

    Regards,
    Yitzhak 

    Friday, January 29, 2010 10:37 AM
  • User-578366845 posted

    Microsoft ASP.NET MVC team members,

    Any thoughts on the subject?

    It looks like that some MVC internal knowledge is needed to overcome the problem.

     

    Regards,
    Yitzhak

    Friday, January 29, 2010 11:38 AM
  • User197322208 posted

    @Yitzhak

    See http://scurt.ro/mvcapplication2.zip - I have put there my application.Works ok. Please tell me what do I want to do to reproduce steps.

    Thank you,


    Friday, January 29, 2010 5:26 PM
  • User-578366845 posted

    Hi ignatandrei,

    Thanks for the sample project.

    I analyzed the provided sample project. A couple of discoveries:

    web.config file needs the following tag:

    <sessionState mode="InProc" timeout="1"/>

    It sets the timeout to one minute.

    Without it you have to wait for 20 minutes which is a default value for timeouts.

     

    Secondly, the sample project is missing very important if condition

     

    string sCookieHeader = Request.Headers["Cookie"];
    
    // if it says it is a new session, but an existing cookie exists, then it
    // must have timed out
    if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
    {
         Response.Redirect("~/home/about");
    }  
    


     

    Unfortunately, after adding the missing if statement the sample project gets into the infinite loop.

    Regards,
    Yitzhak

    Saturday, January 30, 2010 10:34 PM
  • User197322208 posted

    Hello Yitzhak

    Indeed if I add the code you ask, the infinite loop goes.

    And yes, we need an expert on this if we want to know why.I'm not an expert ,but my opinion is that , when we made the request.headers request, some objects ar re-initialized.


    However, I found a simple way to prevent - re-initialize the session  :

    before Response.Redirect, just put

    Session["Andrei"] = "aaa"; // initialize a new session - put in session what do you want...

    So I think your problem is solved!



    Sunday, January 31, 2010 1:38 AM
  • User1854625640 posted

    Response.Redirect("~/controller/action/value")
     

    Hi,

    Try with :

    Response.Redirect("/controllerName/action/value");


     

     

     

    Monday, February 1, 2010 1:52 AM
  • User1679353836 posted

    Hi:

    I have been working on this issue with Yitzhak and he came up the the following solution:

    protected void Session_Start()
            {
                if (Context.Session != null)
                {
                    if (Context.Session.IsNewSession)
                    {
                        string sCookieHeader = Request.Headers["Cookie"];
                        if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                        {
                            // intercept current route
                            HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current);
                            RouteData routeData = RouteTable.Routes.GetRouteData(currentContext);
                          
                            var domainRoute = routeData.Route;
    
                            // substitute route values
                            routeData.Values["controller"] = "homemain";
                            routeData.Values["action"] = "timeout";
    
                            // Clear the error on server.
                            Server.ClearError();
                            Response.Clear();
    
                            // Call target Controller and pass the routeData.
                            IController myController = new Main.Controllers.HomeMainController();
                            myController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    
                            Response.Flush();
                            Response.End();
                        }
                    }
                }
            }

     

    We are puzzled on 2 items:

    1. With the below lines commented:


    // Response.Flush();
    // Response.End();

    the previous page is remembered and displayed as well as the time out page.  All on the same page.  Why is the previous page still being remembered when the time out page is the one being called from the controller 

    2.  With both lines uncommented: 

     Response.Flush();
     Response.End();

    the timeout page is properly displayed but in the address bar it shows the previous pages url.  How do we get the address bar to show the proper url? 

    We have tried Response.Redirect("/controllerName/action/value"); and it is not working. It goes into an infinite loop by constantly creating a new session state.  

    Please advise.


     

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, February 1, 2010 1:26 PM
  • User197322208 posted

    It goes into an infinite loop by constantly creating a new session state.  

    As I said previously, if you put the code

    Session["andrei"]="Value";

    before redirect - it will NOT go into infinite loop!

    Monday, February 1, 2010 3:50 PM
  • User-578366845 posted

    The proposed solution by using MVC's native routing facilities such as routeData and replacing controller and action is working.

    But there are some remaining questions which are still not answered:

    1. Why after calling GetRouteData() in the current context and replacing both routing controller and action,  MVC still goes to the already replaced/original controller and action?
      To prevent this a call to Response.End() is needed.
      What could be done to prevent such phenomena?
    2. How to affect the browser address URL?
      It still shows up the replaced/original controller and action.

     

    Microsoft ASP.NET MVC team members: bradwils, joecar, levib, Eilon, jeloff, and also ricka6, KeFang Chen, please shed some light on the subject.

     

    Regards,
    Yitzhak

    Tuesday, February 2, 2010 1:24 PM
  • User1854625640 posted

    Hi,

    I have tried to reproduce your issue by using the following method. Unfortunately I can't.

    protected void Session_Start(object src, EventArgs e)
            {
                Response.Redirect("/Home/TestAction");
    
            }


    I found that your 'Session_Start' method had no any parameters. So please update the method and try it again. 

    Tuesday, February 2, 2010 8:29 PM
  • User-578366845 posted

    Hi KeFang Chen,

    Just put in your code 3 IF statements and try again. See the sample code. And just inside the 3rd IF put

    Response.Redirect("/Home/TestAction");   

    These 3 IF statements are mandatory to catch timed out session.

    Regards,
    Yitzhak

      

    Tuesday, February 2, 2010 8:59 PM
  • User1854625640 posted

    Hi,

    I have tested it with the following updated code. But I didn't reproduct it either.

     protected void Session_Start(object src, EventArgs e)
            {
                if (Context.Session != null)
                {
                    if (Context.Session.IsNewSession)
                    {
                        string sCookieHeader = Request.Headers["Cookie"];
                        if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                        {
                            // how to simulate it ???   
                            // RedirectToAction(“ActionName”, “ControllerName”,  route values);  
                            Response.Redirect("/Home/TestAction");
                        }
    
                    }
                }
    
    
            }


     Because the last 'if' statement return false, it failed to redirect to the "/Home/TestAction".

    Tuesday, February 2, 2010 10:05 PM
  • User-578366845 posted

     Hi KeFang Chen,

    You are missing one important setting in the web.config file:

    <system.web>
            <sessionState mode="InProc" timeout="1"/>
    


    Just add the sessionState tag to set the timeout value to 1 minute. Otherwise, the default value is 20 minutes. You will need to wait for 1 minute to let the session timeout.

    Regards,
    Yitzhak

    Tuesday, February 2, 2010 10:17 PM
  • User1854625640 posted

    Hi Yitzhak,

    I didn't miss that configuration.  I added the statement at the beginning. But I didn't reproduce the issue.

     

    Tuesday, February 2, 2010 10:29 PM
  • User-578366845 posted

     Hi KeFang Chen,

    I can e-mail you the entire project.

    What is your e-mail address?

     

    Regards,
    Yitzhak

     

    Tuesday, February 2, 2010 10:53 PM
  • User1854625640 posted

    Hi,

    Please send the project to v-kefc@hotmail.com .

    Thanks.

    Tuesday, February 2, 2010 11:23 PM
  • User-578366845 posted

    Below is our final working implementation.

     protected void Session_Start()
    {
         if (Context.Session != null)
        {
               if (Context.Session.IsNewSession)
              {
                  string sCookieHeader = Request.Headers["Cookie"];
                  if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                  {
                    //intercept current route
                    HttpContextBase currentContext = new HttpContextWrapper(HttpContext.Current);
                    RouteData routeData = RouteTable.Routes.GetRouteData(currentContext);
                            
                    //Substitute route Data Token Values for the Area
                    routeData.DataTokens["area"] = "";
                    routeData.DataTokens["UseNamespaceFallback"] = true;
    
                    //substitute route values
                    routeData.Values["controller"] = "homemain";
                    routeData.Values["action"] = "timeout";
    
                    IRouteHandler routeHandler = routeData.RouteHandler;
                    RequestContext requestContext = new RequestContext(currentContext, routeData);
    
                    IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
                    httpHandler.ProcessRequest(Context);
    
                    Response.Flush();
                    Response.End();
                   }
               }
            }
        }
    }
    


    But the original questions are still not answered:

    1. Why after calling GetRouteData() in the current context and replacing both routing controller and action,  MVC still goes to the already replaced/original controller and action?
      To prevent this a call to Response.End() is needed.
      What could be done to prevent such phenomena?
    2. How to affect the browser address URL?
      It still shows up the replaced/original controller and action.

     

    Microsoft ASP.NET MVC team members: bradwils, joecar, levib, Eilon, jeloff, and also ricka6, KeFang Chen, please shed some light on the subject.

     

    Regards,
    Yitzhak

     

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, February 3, 2010 1:44 PM
  • User1406895847 posted

    How about this:

            protected void Session_Start()
            {
                if (Context.Session != null)
                {
                    if (Context.Session.IsNewSession)
                    {
                        string sCookieHeader = Request.Headers["Cookie"];
    
                        if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                        {
                            HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);
                            UrlHelper urlHelper = new UrlHelper(new RequestContext(httpContext, new RouteData()));
                            string redirectUrl = urlHelper.Action("timeout", "homemain");
    
                            httpContext.Response.Redirect(redirectUrl);
                        }
                    }
                }
            }


    Wednesday, February 3, 2010 8:42 PM
  • User1406895847 posted

    How about this:

            protected void Session_Start()
            {
                if (Context.Session != null)
                {
                    if (Context.Session.IsNewSession)
                    {
                        string sCookieHeader = Request.Headers["Cookie"];
    
                        if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                        {
                            HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);
                            UrlHelper urlHelper = new UrlHelper(new RequestContext(httpContext, new RouteData()));
                            string redirectUrl = urlHelper.Action("timeout", "homemain");
    
                            httpContext.Response.Redirect(redirectUrl);
                        }
                    }
                }
            }


    Wednesday, February 3, 2010 8:43 PM
  • User1854625640 posted

    Hi,

    The 'Response.End()' means that all data is writen into the current stream and now we directly exit from the execution. Or else your output to the current stream would be overrided by following output.  

    Wednesday, February 3, 2010 9:33 PM
  • User1406895847 posted

    Sorry for the duplicate post and did not see your last issue. This is the final code, which will not change the original url:

            protected void Session_Start()
            {
                if (Context.Session != null)
                {
                    if (Context.Session.IsNewSession)
                    {
                        string sCookieHeader = Request.Headers["Cookie"];
    
                        if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                        {
                            HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);
                            UrlHelper urlHelper = new UrlHelper(new RequestContext(httpContext, new RouteData()));
                            string redirectUrl = urlHelper.Action("timeout", "homemain");
    
                            httpContext.RewritePath(redirectUrl);
                        }
                    }
                }
            }



    Wednesday, February 3, 2010 10:01 PM
  • User-578366845 posted

    Hi Kazi Manzur Rashid,

    Thanks for stepping in.
    I just tried your proposed implementation. Unfortunately, it is not working. 

    RewritePath() API call for whatever reason is not working properly inside the Session_Start() in global.asax

    Please try it yourself to see what is going on.

    Regards,
    Yitzhak

     

    Thursday, February 4, 2010 12:49 AM
  • User1406895847 posted

    I tried with the following in the default mvc project and when I clicked the About link it takes me back to the Home/Index.

    public override void Init()
    {
        base.Init();
        AcquireRequestState += OnAcquireRequestState;
    }
    
    void OnAcquireRequestState(object sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(Path.GetExtension(Request.FilePath)))
        {
            HttpCookie cookie = Request.Cookies["ASP.NET_SessionId"];
    
            if ((cookie != null) && !string.IsNullOrEmpty(cookie.Value))
            {
                HttpContextBase httpContext = new HttpContextWrapper(Context);
                UrlHelper urlHelper = new UrlHelper(new RequestContext(httpContext, new RouteData()));
    
                string redirectUrl = urlHelper.Action("Index", "Home");
    
                httpContext.RewritePath(redirectUrl);
            }
        }
    }


    Thursday, February 4, 2010 5:34 AM
  • User-578366845 posted

    Hi Kazi Manzur Rashid,

    I commented out the entire Session_Start() and copy/pasted your code as is.

    The outcome is the same - it is not working. The RewritePath() is not functioning properly in the global.asax

    Regards,
    Yitzhak

     

    Thursday, February 4, 2010 7:44 PM
  • User1406895847 posted

    Try create a default asp.net mvc project and put the above code. Not sure why it is not working with your version. 

    Friday, February 5, 2010 8:40 AM
  • User-578366845 posted

    Hi Kazi Manzur Rashid,

    Please provide your e-mail address so I will be able to e-mail you the entire VS solution with the default MVC project.

    Regards,
    Yitzhak 

    Friday, February 5, 2010 9:57 AM
  • User-699059308 posted

    Hi Ignatandrei,

    Can u please help me on session time out.

    Requirement:
    When session timeout alert should be shown to user then it sholud redirect to login page.

    please provide code  for this.

    Thanks,
    Ranjith.

    Thursday, May 13, 2010 3:50 AM
  • User-864992836 posted

    HI 

     can you please send this code to me at 'miansaqibali@yahoo.com'



    Thanks,

    -Saqib

    Monday, December 27, 2010 6:56 AM
  • User-1136685341 posted
    Is there any workaround if it is a cookieless state?
    Monday, March 7, 2011 9:41 AM
  • User197322208 posted

    MVC does not support cookieless state

    Monday, March 7, 2011 10:21 AM
  • User1732369556 posted

    I also have similar problem. now finding solution too.

    Friday, October 18, 2013 3:27 AM