Saturday, June 5, 2010

Preventing Internet Explorer from caching AJAX requests when using GWT RequestBuilder

I recently spent the best part of a few days trying to work out why some GWT HTTP code I had written was not working (well specifically why it was not working on Internet Explorer). The code was talking to an existing server API that used HTTP GET for a handshake to establish a session-id with the server, and thereafter used HTTP POST to retrieve data. The problem occurred when it attempted to re-handshake. It would always return the same session-id, whereas the server would expect a different session-id. It was all very confusing, until I did some HTTP level tracing and noticed that past the first GET call, the server would never see another GET request.

It turns out, for reasons best known to itself, Internet Explorer will aggressively cache GET requests when using the XMLHttpRequest object. When writing an AJAX application this is particularly painful if you want your result to be dynamic.

If you can modify the server, the best approach is to ensure that the server correctly sets the Cache-Control: no-cache header, but this is not always possible if you are working with a legacy backend.


Another solution is to use an HTTP POST instead, but again that's a problem if you working with existing code, or you are writing to a REST API and you want to keep the GET = Read and POST = Create idiom.


If you want to force the client to get it right this narrows your choices.

A commonly suggested approach is to generate a random request parameter and add this to your request either by appending ?requestId=r4nd0m-v4lu3 to the URI, or if your request already includes parameters appending &requestId=r4nd0m-v4lu3. While this means you won't see the cached responses, the browser still does put these items in the cache, and if you are doing a large number of these requests, this will start to evict things that you actually want to be in there! It may also be a problem if your application enforces strict checking of the passed in parameters.

The best approach I have found is to use the If-Modified-Since header with the date set to the Epoch (1970-01-01 00:00:00 GMT). The browser will not cache the result, and you will not have to worry about clashing with existing parameters. The nice thing about this approach is it is a general solution which you can put in a library and then not worry about problems with the backend.

To do this when using the Google Web Toolkit (GWT) RequestBuilder interface it is simply a matter of using the setHeader method as follows:

void nonCachingHttpGet(String uri, String data, RequestCallback callback)
     RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, uri);
     // This header is required to force Internet Explorer to not cache values from
     // the GET response.
     builder.setHeader("If-Modified-Since", "01 Jan 1970 00:00:00 GMT");
     builder.setHeader("Content-type",
                       "application/x-www-form-urlencoded");
     builder.setCallback(callback);
     builder.send();
}