Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Session Timeout Warning and Redirect

4.92/5 (15 votes)
11 Aug 2011CPOL4 min read 150.3K   4K  
How to warn the user before the session expires, and redirect to a new page if it does.

Image 1

Introduction

This article teaches you how to use ASP.NET (C#) to warn the user about a pending session timeout, provides an opportunity to keep the session alive, and displays another message (or redirects the user) if there is no response and the session actually does timeout.

Background

Most ASP.NET websites make use of the (server-side) "Session" to maintain state and allow information to be preserved as the user navigates between pages using the inherently stateless HTTP protocol. A common issue faced by such websites is how to handle session timeouts. Through version 3.5, the .NET Framework does not provide an out-of-the box method for notifying the user when a timeout occurs. Without adding code to handle this scenario, the app is likely to throw an ugly NullReferenceException (object reference not set to an instance of an object).

Some solutions offered involve warning the user of a pending session timeout by using the JavaScript "alert" or "confirm" functions, but these have two main drawbacks. First, the dialogs cannot be styled and will not fit in with the look and feel of the website. Second, if the dialogs sit up on the screen for a long time without being acknowledged by the user, the session may timeout even if the user wishes to continue. And there is not a good way to withdraw the dialogs or replace the messages with a message saying that the timeout has actually occurred.

Other solutions may involve a full page postback, which is rarely desirable as it interferes with the user experience.

Here I will present a full solution which solves these issues and should be applicable in most situations. The strategy will be:

  • 0. Incorporate the warning logic on a master page, so all content pages may benefit from it.
  • 1. Use JavaScript to activate a client-side function before the session times out.
  • 2. Have that function display a div tag which includes a warning message and a keep-alive button. This div tag can be styled however you want using CSS.
  • 3. Place the keep-alive button inside an UpdatePanel to cause the server to asynchronously refresh the session timeout window (without a postback).
  • 4. Use another scheduled JavaScript function for the case in which the user never responds to the initial warning. This function can either change the message in the div tag to tell the user it's too late, or it can navigate the user to another web page entirely.

Using the Code

Master Page ASPX

Since seeing is believing, let's start with the master page and the visual components. In the HTML, our div tag divTimeoutWarning contains the initial warning message and a button. We give it a position of Fixed to be sure it is positioned relative to the browser window and not subject to scrolling. We also have a ScriptManager control on the page so we can use an UpdatePanel. This is the regular non-visual control available in the Toolbox in the "AJAX Extensions" group.

Now looking at the JavaScript, our first function BodyOnLoad() registers interrupts which will be activated before, and when, the session expires. Next, ShowSessionTimeoutWarning() simply gets a reference to the div and changes the display attribute to inline so the div will appear. The function ShowSessionExpiredNotification() is activated if the user fails to acknowledge the initial warning. It can be used to show a new message or it can be used to redirect the user to any page via the JavaScript window.location property. When the user does press the button to keep his session alive, a very simple AJAX request is sent to the server. When the server receives this, it automatically refreshes the session window. We use a client side click function ResetClientSideTimers() to re-hide the div tag by setting its display attribute to none, and we reset the timeouts for our warning functions so we can repeat the process all over again.

ASP.NET
<%@ Master Language="C#" AutoEventWireup="true" 
    CodeBehind="Site1.master.cs" Inherits="WebApplication1.Site1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Master Page</title>

    <script type="text/javascript" language="javascript">

        //Set timeouts for when the warning message
        //should be displayed, and what should happen 
        //when the session actually expires.
        function BodyOnLoad()
        {
            setTimeout('ShowSessionTimeoutWarning()', '<%=iWarningTimeoutInMilliseconds%>');
            setTimeout('ShowSessionExpiredNotification()', '<%=iSessionTimeoutInMilliseconds%>');
        }

        //Notify the user that his session is ABOUT to expire.
        //Do so by making our warning div tag visible.
        function ShowSessionTimeoutWarning()
        {
            var divSessionTimeoutWarning = 
                document.getElementById('<%=divSessionTimeoutWarning.ClientID%>');

            divSessionTimeoutWarning.style.display = 'inline';
        }

        //Notify the user that his session HAS expired.
        function ShowSessionExpiredNotification()
        {
            var divSessionTimeoutWarning = 
                document.getElementById('<%=divSessionTimeoutWarning.ClientID%>');

            //Send the user to a new page.
            window.location = '<%=sTargetURLForSessionTimeout%>';

            //To tell the user that his session has expired, WITHOUT redirecting, 
            //remove the above line, and uncomment this section:
            ////Re-use the existing label, but change the text.
            //var lblSessionWarning = document.getElementById('<%=lblSessionWarning.ClientID%>');
            //lblSessionWarning.innerText = 'Your session has expired. You are SOL.';
            ////Hide button.
            //var btnContinueWorking = document.getElementById('<%=btnContinueWorking.ClientID%>');
            //btnContinueWorking.style.display = 'none';
        }

        function ResetClientSideSessionTimers()
        {
            var divSessionTimeoutWarning = 
                document.getElementById('<%=divSessionTimeoutWarning.ClientID%>');
            divSessionTimeoutWarning.style.display = 'none';

            //Reset timers so we can warn the user the NEXT time the session is about to expire.
            setTimeout('ShowSessionTimeoutWarning()', '<%=iWarningTimeoutInMilliseconds%>');
            setTimeout('ShowSessionExpiredNotification()', '<%=iSessionTimeoutInMilliseconds%>');
        }
    </script>

    <asp:ContentPlaceHolder ID="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body onload="BodyOnLoad()">
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div>
        <%--In a real application, use a CSSClass and set these 
                 display properties in a CSS file, not inline.--%>
        <div id="divSessionTimeoutWarning" runat="server" 
                  style="position: fixed; left: 250px; top: 100px; 
                         background-color: Yellow; border-style: solid">
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <ContentTemplate>
                    <asp:Label ID="lblSessionWarning" runat="server" 
                       Text="Warning.  Your server session is about to expire due to inactivity."
                    ></asp:Label>
                    <br />
                    <asp:Button ID="btnContinueWorking" runat="server" Text="Continue Working" 
                       OnClientClick="ResetClientSideSessionTimers()" 
                       OnClick="btnContinueWorking_Click" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </div>
        <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
        </asp:ContentPlaceHolder>
    </div>
    </form>
</body>
</html>

Master Page C# Code-Behind

Now let's take a look at the code-behind for the master page. First off, we use Session.Timeout to pull the timeout value from the web.config file. Since this setting is in minutes (20 by default), and since our client-side functions expect milliseconds, we do a little math and store the results in two public variables so they can be accessed from the master page's ASPX. We set the display attribute for the div to "none" so it isn't displayed when the page first comes up, and we do this in C# so that the div is visible in Visual Studio at design time. The btnContinueWorking_Click() method is called by our keep-alive button which is inside an UpdatePanel. Receipt of the AJAX message itself will refresh the session, so the Click method doesn't need to run any code.

C#
using System;
using System.Configuration;

namespace WebApplication1
{
    public partial class Site1 : System.Web.UI.MasterPage
    {
        //Public values here can be late-bound to javascript in the ASPX page.
        public int iWarningTimeoutInMilliseconds;
        public int iSessionTimeoutInMilliseconds;
        public string sTargetURLForSessionTimeout;

        protected void Page_Load(object sender, EventArgs e)
        {
            //In a real app, stuff these values into web.config.
            sTargetURLForSessionTimeout = "Error.aspx?Reason=Timeout";
            int iNumberOfMinutesBeforeSessionTimeoutToWarnUser = 1;

            //Get the sessionState timeout (from web.config).
            //If not set there, the default is 20 minutes.
            int iSessionTimeoutInMinutes = Session.Timeout;

            //Compute our timeout values, one for
            //our warning, one for session termination.
            int iWarningTimeoutInMinutes = iSessionTimeoutInMinutes - 
                iNumberOfMinutesBeforeSessionTimeoutToWarnUser;

            iWarningTimeoutInMilliseconds = iWarningTimeoutInMinutes * 60 * 1000;

            iSessionTimeoutInMilliseconds = iSessionTimeoutInMinutes * 60 * 1000;

            if (!this.IsPostBack)
            {
                //Don't show the warning message div tag until later.
                //Setting the property here so we can see the div at design-time.
                divSessionTimeoutWarning.Style.Add("display", "none;");
            }
        }

        protected void btnContinueWorking_Click(object sender, EventArgs e)
        {
            //Do nothing. But the Session will be refreshed as a result of 
            //this method being called, which is its entire purpose.
        }

    } //class
} //namespace

That should do it. The redirect target page can say whatever you want, and the full source code is available via the link at the top of this article. Enjoy.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)