Just when you thought that Ajax was paving the way to a richer
and more interactive browsing experience, it has run into some resistance
from proponents of the Web 2.0 paradigm. In a nutshell, Web 2.0 is about making
websites machine-readable so that content can be shared seamlessly between
unrelated sites. The rich client goal of Ajax-based frameworks runs contrary
to the Web 2.0 ideals in many ways. Web developers who now find themselves
in the crossfire. So what's a developer to do? Stick with straight HTML, CSS,
and a bit of JavaScript to enable easy data sharing or use Flash to emulate
a desktop application - thus fostering a richer client experience? Personally,
I am all for ease of data sharing, but I believe that the Web should be about
people first. Today we'll be
discussing the use of Ajax in client-side caching. It's really a two pronged
topic: first, it is beneficial for performance to cache the data once it's
been downloaded because it minimizes server calls; second, you have to be
able to force the browser to refresh some content when a server call is made.
We tackled the first issue in Building a Client-Side Ajax Cache.
This article will concentrate on the second one concerning content refreshing.
Before we get started, you may want to brush up on
the basics of Ajax, since this article assumes that you already have a working
knowledge of it.
Using Ajax to Refresh Dynamic Content
A typical usage for Ajax is to refresh a portion of the page
at a set interval. For example, imagine a page that contains the current weather
conditions. The latest conditions should be updated every so often, say every
five minutes. The Prototype Framework has just the thing: it's called the
PeriodicalUpdater
. All we need to do is supply it with the ID
of the page
element that we want to update, the URL
of the server resource, and a JavaScript
object literal containing some optional parameters. We need to supply the
frequency
because it differs from the default of every two seconds. We'll
attach it to the window's onload
event using the Event.observe()
method. It
takes the object, the event name (without the 'on'), and the function to bind
to it:
Date
object and/or
Math.random()
. Here is our PeriodicalUpdater
with a random number ID appended
as a query parameter called sid
:
This will prevent the page from being cached, but there is a downside in that each request will now be stored in the cache, adding useless and never reused content that could be better utilized for more static pages. Not to mention, more useful data will be purged from cache to make way for these one-time responses.
A less obtrusive solution is to set the server call's request
headers in such a way as to prevent caching of the dynamic content. The idea
is basically to trick the browser into thinking that the content has expired
so that a trip to the server is necessary. Most browsers implement some sort
of caching, but there are differences in how and when the cached data is revalidated.
For instance, Firefox revalidates the cached response every time the page
is refreshed, issuing an If-Modified-Since
header with value set to the
value of the Last-Modified
header of the cached response. Internet Explorer,
on the other hand, does so only if the cached response is expired (I.E., after
the date of received Expires
header). The following set of headers should
work on all major browsers:
In the above example, we modified the previous Ajax call to
include the relevant request headers. Prototype allows you to include custom
HTTP request headers using the requestHeaders
option. It accepts name-value
pairs as a hash (as in our example) or in a flattened array, like: ['X-Custom-1',
'value', 'X-Custom-2', 'other value']
.
A Brief Lesson on Request Header Fields
As per the HTTP protocol, a request message may contain one
or more header fields, which are formatted one line per header, in the form
Header-Name: value
, ending with a Carriage Return and a Line Feed (CRLF).
There are forty-six headers defined by HTTP 1.1, but only one - the Host
-
is required in requests. Usually, the From
and User-Agent
fields are also
sent, at a minimum. The headers that interest us are those that deal with
caching.
Typically, a request header field will contain the name of the
field and its directive(s), or instruction(s), separated by a colon. Each
header has its own set of possible directives to choose from. Multiple directives
can be provided by separating each by a comma (,). Some headers take name=value
pair tokens instead of directives. Here is the format for the Pragma
directive: