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.
<%@ 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">
function BodyOnLoad()
{
setTimeout('ShowSessionTimeoutWarning()', '<%=iWarningTimeoutInMilliseconds%>');
setTimeout('ShowSessionExpiredNotification()', '<%=iSessionTimeoutInMilliseconds%>');
}
function ShowSessionTimeoutWarning()
{
var divSessionTimeoutWarning =
document.getElementById('<%=divSessionTimeoutWarning.ClientID%>');
divSessionTimeoutWarning.style.display = 'inline';
}
function ShowSessionExpiredNotification()
{
var divSessionTimeoutWarning =
document.getElementById('<%=divSessionTimeoutWarning.ClientID%>');
window.location = '<%=sTargetURLForSessionTimeout%>';
}
function ResetClientSideSessionTimers()
{
var divSessionTimeoutWarning =
document.getElementById('<%=divSessionTimeoutWarning.ClientID%>');
divSessionTimeoutWarning.style.display = 'none';
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>
<%----%>
<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.
using System;
using System.Configuration;
namespace WebApplication1
{
public partial class Site1 : System.Web.UI.MasterPage
{
public int iWarningTimeoutInMilliseconds;
public int iSessionTimeoutInMilliseconds;
public string sTargetURLForSessionTimeout;
protected void Page_Load(object sender, EventArgs e)
{
sTargetURLForSessionTimeout = "Error.aspx?Reason=Timeout";
int iNumberOfMinutesBeforeSessionTimeoutToWarnUser = 1;
int iSessionTimeoutInMinutes = Session.Timeout;
int iWarningTimeoutInMinutes = iSessionTimeoutInMinutes -
iNumberOfMinutesBeforeSessionTimeoutToWarnUser;
iWarningTimeoutInMilliseconds = iWarningTimeoutInMinutes * 60 * 1000;
iSessionTimeoutInMilliseconds = iSessionTimeoutInMinutes * 60 * 1000;
if (!this.IsPostBack)
{
divSessionTimeoutWarning.Style.Add("display", "none;");
}
}
protected void btnContinueWorking_Click(object sender, EventArgs e)
{
}
}
}
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.