Strategies for handling session timeout?

Strategies for handling session timeout?

Rusty BallingerRusty Ballinger Posts: 21Questions: 7Answers: 0

Apologies for the long question; this is half "how do I get this to work," and half "do I even want this to work?"

I have a case where this is happening:

  • the user logs in, loads a page with a DataTable, & brings up one or more records in Editor
  • they wander off; their session times out
  • they come back and hit submit
  • the server sends back an HTTP 302 redirect to the login page (and the login page is being requested; more on this below)
  • Editor displays "A system error has occurred" as the error message, with a "more information" link to datatables.net

My first thought was that I wanted to display a more helpful error message ("Dude, you need to change your fiber intake" or whatever). However, it turns out that things are actually a little more complicated than that: some of my Editor fields use dependent to rebuild option lists when their values change (e.g., if you're trying to assign Necrobutcher a role in a black metal band, and you choose Mayhem as the band, I fetch Mayhem's list of available roles, which will have Lead Keytar filtered out on the server side), and those ajax calls are getting 302s (followed by 200s as the browser--or jQuery?--succesfully requests the login page from the redirect, and then my code croaks when it gets the HTML login page instead of the JSON list of roles).

So, in some cases, life may be bad before they hit submit, so wanting to handle the 302 in Editor's "submit" ajax call may be fundamentally misguided. How are other people handling this sort of thing?


If it turns out that the "submit" ajax call is the right place to handle the 302, I see this question which gives an example of handling an error by calling alert() and then notifying Editor, and I see the ajax documentation explaining how to use a function instead of an object, but I'm having trouble getting it working.

Here's what I've got currently, before making any changes.

//  this guy is in a file which is shared by all my DataTables pages
var editorAjax = function( url ) {
    return {
        'url': url,
        contentType: 'application/json',
        data: function ( d ) {
            return JSON.stringify( d );
        }
    };
};

//  this is how I'm calling that in my various tables/pages
editor = new $.fn.dataTable.Editor( {
    ajax: editorAjax( '/my/nifty/api' ),
    table: '#my_nifty_table',
    ...

Adding an error element to that object returned by my editorAjax() function looks like this:

var editorAjax = function( url ) {
    return {
        'url': url,
        contentType: 'application/json',
        data: function ( d ) {
            return JSON.stringify( d );
        },
        error: function ( xhr, jqAjaxerror, thrown ) {
            if ( xhr.status == 302 ) {
                //  call error() on my editor instance, once I get my
                //  hands on it?  (It doesn't exist at this point;
                //  editorAjax() is called on the way in to the Editor
                //  constructor.)
            }
            //  what else do I need to do in here?
        }
    };
};

The main problem here is that this doesn't actually get called with the 302! It looks like, under the covers, the browser is following the redirect & fetching the login page, and then this gets hit with the jQuery JSON.parse error when it tries to parse the login page HTML as JSON. (And at that point, xhr.status is 200, because the second HTTP request succeeded!)

Returning a function instead of an object behaves the same, and looks like this:

var editorAjax = function( url ) {
    return function ( ignoredMethod, ignoredURL, data, success, error ) {
        $.ajax( {
            type: 'POST',
            url:  url,
            contentType: 'application/json',
            data: JSON.stringify(data),
            dataType: 'json',
            success: function ( json ) {
                success( json );
            },
            error: function ( xhr, jqAjaxError, thrown ) {
                //  we don't get xhr.status == 302 in here...
                error( xhr, jqAjaxError, thrown );
            }
        } );
    };
};

So, if (IF!) handling the session timeout here is the right thing to do... how do I do it? And if not, how should I handle it?

Answers

  • allanallan Posts: 61,743Questions: 1Answers: 10,111 Site admin

    Necrobutcher

    Great example :)

    Rather than attempting to do this on a per $.ajax call, which is sure to lead to madness, I'd suggest using jQuery's global Ajax event handlers - specifically the ajaxError handler. There you can just dump them back to the login page and call it done for the whole page.

    ALlan

  • Rusty BallingerRusty Ballinger Posts: 21Questions: 7Answers: 0

    Necrobutcher

    Great example :)

    (Well, the truth is, my production data is all 90's boy bands, but I didn't want people to find out I wasn't metal.)

    Rather than attempting to do this on a per $.ajax call, which is sure to lead to madness, I'd suggest using jQuery's global Ajax event handlers

    Ah, thanks!

    These aren't exactly working for me--I can see my handlers for ajaxSuccess() and ajaxComplete() get hit when the page first loads, but after the session times out, it looks like the 302 redirect is being followed automatically, and the "successful" load of the login page hits the $.ajax({ ... success: function (...) { on my Editor's initCreate/initEdit and croaks (in my code, when it hits the unexpected login page HTML) without ajaxError(), ajaxSuccess(), or ajaxComplete() getting called.

    Still, this seems like a step in the right direction; maybe jQuery.ajaxTransport() will let me see when a 302 is received.

  • allanallan Posts: 61,743Questions: 1Answers: 10,111 Site admin

    It looks like XMLHttpRequest will automatically follow redirects (unfortunately in this case). I couldn't find explicit mention of that in the spec, but this section does say:

    All redirects (if any) have been followed and all HTTP headers of the response have been received.

    So I think what you might need to do here is instead of returning 302, return 401 Not Authorized for this sort of error. Then the client-side can see that 401 and redirect the browser itself.

    Allan

This discussion has been closed.