a little madness

A man needs a little madness, or else he never dares cut the rope and be free -Nikos Kazantzakis

Zutubi

Ajax vs Caching

Recently I’ve thrown some simple Ajax (well, Aj at least) into our app, to refresh content that changes frequently without refreshing the whole page. With a library like prototype, it really couldn’t be simpler, just throw in an Ajax.PeriodicalUpdater that pulls in a new HTML fragment and you’re set.

Almost.

Working with web UIs, we’ve all run into the standard browser caching problems. A meta tag or two:

<meta http-equiv=”Pragma” content=”no-cache”/>
<meta http-equiv=”Expires” content=”0″/>

solves most problems. When you start using Javascript to periodically modify the page, however, things get trickier. The first problem I ran into was with IE. Symptom: the periodical updates just didn’t work. Because these were just fragments of a page, they didn’t contain the meta tags above. IE was caching the HTML fragment rather than hitting the server again as desired. Solution: use the no-cache and expiration headers on the HTTP response itself. I implemented this in WebWork with a simple interceptor, keeping this detail out of my action classes and allowing it to be reused as necessary:

public class AjaxInterceptor extends AroundInterceptor
{
    protected void after(ActionInvocation actionInvocation, String string)
        throws Exception
    {
    }

    protected void before(ActionInvocation actionInvocation)
        throws Exception
    {
        HttpServletResponse response = ServletActionContext.getResponse();

        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);
    }
}

So we’re done right? Right? Wrong. This fixed the original IE problem, but after a while I noticed another major annoyance. I had committed a mortal web UI sin: the back button was broken.

If an updating page was left open long enough for a content refresh, navigating away from that page and then returning via the back button displayed the originally-loaded content, not the refreshed content. From a user’s perspective this is not only annoying, but downright confusing. The problem in this case was again caching: not caching of the HTML fragment, but caching of the whole page. “But wait!”, you ask, “That original page has the meta tags, right?”. Right! But the back button doesn’t care. In fact, the HTTP spec requires the back button not to care. When you hit that back button you get the originally-loaded content, from the browser’s cache, expiration rules be damned!

So what can we do? The answer is to stop the page being put in the cache in the first place. This can be done with a liberal sprinkling of Cache-Control headers, to cater for various browsers:

...
    protected void before(ActionInvocation actionInvocation)
        throws Exception
    {
        HttpServletResponse response = ServletActionContext.getResponse();

        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "must-revalidate");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Cache-Control", "no-store");
        response.setDateHeader("Expires", 0);
    }
...

Firefox in particular is fussy, requiring “no-store”.

For more information on back button and caching behaviour in Firefox and IE, here’s a head start:

——–
Into continuous integration? Want to be? Try pulse.

Liked this post? Share it!

5 Responses to “Ajax vs Caching”

  1. May 16th, 2006 at 11:34 am

    BarelyBlogging » Blog Archive » links for 2006-05-16 says:

    [...] Ajax vs Caching How to ensure your AJAX request is not cached. (tags: ajax cache) [...]

  2. February 27th, 2007 at 8:24 pm

    Graham says:

    Thanks for this. Nowhere else seems to explain the problem (or indeed that there is a problem)

  3. December 29th, 2007 at 7:46 am

    gaurav says:

    good work

    i had the same problemm wid the ajax updater………………but nw its seems dat i hv the solution

    good work dude

  4. July 30th, 2008 at 2:01 am

    a little madness » Ajax vs Caching vs Firefox 3 says:

    [...] an earlier post, many moons ago, I related some struggles with Ajax page updates and browser caching behaviour. [...]

  5. February 14th, 2009 at 12:15 am

    Douglas says:

    Man, I tried all these things you said, but I couldn’t fix this caching issue using AJAX.
    Now I’m using a simple workaround to make IE do not recognize my request as being the same:

    To every AJAX request created, I append to it a parameter called “uid” just like the followint:

    var action = “action.do”; // your normal action string
    var date = new Date();
    var URL = action + “&uid=” + date.getTime();
    xmlHttp.open(“GET”, actionURL, true);
    xmlHttp.send(null);

    This date.getTime() function returns time in milliseconds, so, the request will never hava the same URL string!

    I gave up to unable caching. Either way, this way I don’t need to worry about various kinds of browsers (IE, FF, Opera, Safari…).

    Regards,
    Douglas.

Leave a Reply