Home  >  

Detecting an End of Session event with jQuery

Author photo
AddThis Social Bookmark Button
Last week I discussed jQuery's ajaxSetup feature. This is a way to setup global event handlers for all AJAX based events on a page. Today I whipped up what I think is a pretty good practical example of this - handling session expiration.

With AJAX applications giving us a lot more power than an "old school" web 1.0 page, it isn't unusual for someone to just sit on one page and fire off various operations that use HTTP to fetch and present data. This works fine until you leave the site alone for too long and your session times out. (I'm assuming most folks use a time based session, much like how ColdFusion works.) The question is - what happens in your current AJAX based application when a user's session times out? For a site that requires authentication, the results can be unexpected. If your working with JSON, then a response that contains the HTML of a login form certainly will not be a valid result. Even if you are working with simple HTML strings back and forth, it's going to be a bit wonky if all of a sudden a login form appears in the middle of a div tag that is supposed to show other results. Let's look at a simple example of this.

I've created a simple ColdFusion application that requires a login before using the site. You can view this demo here. The login form is prepopulated with the right username and password, but just in case it doesn't work, use "admin" and "paris". Once logged in, you can enter a simple string into the text box. Hit submit, and jQuery will do a quick AJAX call to a ColdFusion file that reverses the string. (Yes, this is overkill, and yes, you can do this in JavaScript, but I wanted something quick and dirty to test with.)

The session is set to timeout after one minute, so if you wait a good 60 seconds and try to search again, you will see something weird...

rkcPicture 1.png

The login screen is now embedded in the page itself. While it may actually work in there, it probably isn't desirable. So how can we go about handling this? My first attempt was to use a method I saw used in the Spry AJAX framework. They have support for looking for a particular string response from the server. If that response occurs, then it is assumed that a session has ended, and you can register an event handler to support that. What's cool is - we can use another jQuery feature that I blogged about a few weeks ago to detect an AJAX based response. The following code is ColdFusion, but could obviously be done in other server side languages as well. It runs with the Application.cfc/onRequestStart context, which basically means before every request:

<cfif not structKeyExists(session, "loggedin") and listLast(arguments.thePage,"/") neq "login.cfm">
			
	<!--- is it jquery? --->
	<cfset reqData = getHTTPRequestData()>
	<cfif structKeyExists(reqData.headers,"X-Requested-With") and reqData.headers["X-Requested-With"] eq "XMLHttpRequest">
		<cfoutput>session expired</cfoutput><cfabort>
	<cfelse>
		<cfinclude template="login.cfm">
		<cfabort />
	</cfif>
			
</cfif>

This code basically says the following: If we need to login, check and see if the jQuery request header, "X-Requested-With", exists and is the right value. If so, output "session expired" and kill the request. Otherwise, respond with a login form. Now we can handle expired sessions coming from both 'normal' and Ajax-based requests, and both will securely be aborted.

Let's look at the front end code:

<html>

<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$(document).ready(function() {
 	
 	$.ajaxSetup({
  		success:function(data,textstatus) {
   			data = $.trim(data)
   			if(data == 'session expired') {
    				alert('Sorry, your session has expired.')
    				window.location.reload(true)	
    			}
   		}
  	})
 
 	$("#mainForm").submit(function() {
  		var str = $("#string").val()
  		console.log('will reverse '+str)
  		$("#response").load('reverse.cfm',{string:str})
  		return false
  	})	
})
</script>
</head>

<body>

<h2>Ajax Test</h2>
	
<p>
Enter a string in the field and I'll make an AJAX request to reverse it.
</p>
	
<form id="mainForm">
	<input type="text" name="string" id="string"> <input type="submit" value="Reverse">
</form>

<div id="response"></div>

</body>
</html>

Obviously you can ignore the vanllia HTML stuff, as well as the submit handler. It just calls my ColdFusion code to handle the reverse operation. But pay attention to the ajaxSetup call. I've defined a success function that will run when the AJAX request successfully completes. If the response is "session expired", then we alert the user and reload the entire page. (Alerts are evil - normally I'd use a jQuery UI Dialog.) You can demo this here. Again, you want to login with admin/paris, play with the reverser a bit, and then sit idle for a good minute or two. Two things bug me about this though. One, it is certainly possible someone may want to reverse the reverse of session expired. Who would? Probably me. Secondly, you do actually see 'session expired' in the result for a split second before the page reloads. All together it just feels hackish.

I did some more digging and it occurred to me - I'm using the presence of a header on the server side to detect a jQuery call. Why not use a header to detect a session expired response? I modified my ColdFusion code to do just that:


<!--- is it jquery? --->
<cfset reqData = getHTTPRequestData()>
<cfif structKeyExists(reqData.headers,"X-Requested-With") and reqData.headers["X-Requested-With"] eq "XMLHttpRequest">
	<cfheader name="sessionexpired" value="1">
	<cfabort>
<cfelse>
	<cfinclude template="login.cfm">
	<cfabort />
</cfif>
 

Again, even if you aren't a ColdFusion person, you can probably read that well enough to see that for jQuery responses that have timed out, we now respond with a custom header and no text at all.

On the front end, I have a slighly modified ajaxSetup call:


$.ajaxSetup({
 	complete:function(req) {
  		if(req.getResponseHeader('sessionexpired') != null) {
   			alert('Sorry, your session has expired.')
   			window.location.reload(true)					
   		}
  	}
})

I'm using the complete method to look for the sessionexpired response header. If it exists, I fire off the alert and do the page reload. You can demo this here.

Thoughts? Useful example?

Read more from Raymond Camden. Raymond Camden's Atom feed cfjedimaster on Twitter

Comments

2 Comments

Pete Kucera said:

Ray,

Does an AJAX call to a local component refresh the ColdFusion session? At this time, my component simply returns a string. I created a session timer using JavaScript and JQuery that puts an alert on the screen with the users session is about to expire. Of course the timer is a JS variable set a minute before the actual CF session expires. The alert has a continue button that fires the AJAX call and restarts the timer. The problem is that my CF session does not seem to extended. Any help?

Thanks,

Pete

Raymond Camden said:

Yes, it does keep the same session. The HTTP request includes the same cookies from your main browser request. I'd say you have another issue in play. If you would like, I can send you some files I just wrote to confirm this.

Leave a comment


Tag Cloud

iPad

What's your take on the iPad? (Putting aside the Flash/iPad flame war)

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.