Handling Expired Sessions via AJAX & FW/1

This is a followup to my “Framework One AJAX Method (FW/1)” post (https://christierney.com/2012/07/14/framework-one-ajax-method-fw1/).

Scenario:

  1. You use the session scope to define if a user is logged in or not
  2. You use jQuery AJAX to pull JSON data from FW/1 action URL’s
  3. The user’s session has expired after x minutes of inactivity after login
  4. If the session is expired the user is directed to a login page after trying to navigate

So what happens in this scenario? Instead of the expected JSON data your AJAX call receives the HTML of a login page with a status of 200. Can’t do too much with this.

Here’s a code example that will pass the client a 403 error (Forbidden) in the header and return no content. jQuery will then redirect the user to a login screen when it sees this status code.

First here’s a simlified FW/1 Application.cfc setupRequest() method:

void function setupRequest() {

	var reqData = getHTTPRequestData();

	if( structKeyExists( reqData.headers, 'X-Requested-With' ) && reqData.headers[ 'X-Requested-With' ] == 'XMLHttpRequest' && !structKeyExists( session, 'user' ) ) {
		getpagecontext().getresponse().setstatus( 403 );
		abort;
	}
}

This code detects if the call came from an AJAX request ( getHTTPRequestData().headers.X-Requested-With = ‘XMLHttpRequest’ ) and if the session still knows about the user. If it is an AJAX request and the user is not known, then set the status code of the return page to 403 and stop processing any more code. If you try to use throw instead of abort, it will overwrite the status code to 500.

The second simple example is the jQuery piece:

$( document ).ready( function() {

	$( this ).ajaxError( function( e, jqXHR, settings, exception ) {
		if( jqXHR.status == 403 ) {
			location.href = '?logout';
			throw new Error( 'Login Required' );
		} else if( !jqXHR.statusText == 'abort' && jqXHR.getAllResponseHeaders() ) {
			alert( 'There was an error processing your request.\nPlease try again or contact customer service.\nError: ' + jqXHR.statusText );
		}
	});

});

Here we are globally looking at all AJAX requests. Since the status code 403 is in the error class it will throw an error. The .ajaxError() method picks up this error and handles it.

If the status code is detected as a 403 (which we set in our ColdFusion code) then we direct the user to a logout page (which in turn directs to a login page) and throws a JS error. The throw statement is supposed to stop all JS processing, however if you have an error handler attached to the specific AJAX call, then that will still fire. The error message will just be seen if you are viewing the JS console.

If there’s another error caught it first looks to see if the request was aborted or if the user navigated away from the page. In these two cases I don’t want to display an error. If anything else is caught, I display a generic message.

#ajax, #coldfusion-2, #fw1, #jquery, #session

Global AJAX Loading Spinners

Many sites include what are known as AJAX Spinners. They are little graphics that show up on a web page that show something is happening behind the scenes. This is most commonly used when an AJAX request is being made and the end-user is waiting for a response.

There are many different ways you can make this happen.

One is to show and hide the spinner inside each AJAX method. Another is to show and hide the spinner inside the jQuery $.ajaxSetup configuration.

If you load individual spinners for each call this is a pain and can be cluttering. If you load a common spinner you can end up hiding it before a parallel request is finished unless you look at a queue, which is also a pain.

One easy way around all this confusion is to use jQuery’s global AJAX handlers: .ajaxStart and .ajaxStop (also look at .ajaxError)

First setup a common spinner container and graphic.

<style>
#ajaxSpinnerContainer {height:11px;}
#ajaxSpinnerImage {display:none;}
</style>

<div id="ajaxSpinnerContainer">
<img src="/images/ajax-loader.gif" id="ajaxSpinnerImage" title="working...">
</div>

One great place to generate these spinners is ajaxload.info.

The next step is to show the spinner when any AJAX request has begun and hide it when all AJAX requests have completed.

$(document)
.ajaxStart(function(){
    $("#ajaxSpinnerImage").show();
})
.ajaxStop(function(){
    $("#ajaxSpinnerImage").hide();
});

This quick and easy method avoids all the other headache associated with all the other techniques out there as long as you want the spinner shown in a single container.

#ajax, #jquery, #spinner