ASP.Net Caching

6/27/2008

First off, I'm not going to be talking about saving an item to the view state, session state, etc. I'm talking about caching an item (for instance an HTML page) on the client's machine. And for anyone that has access to the IIS server itself and can turn the content expiration on or make it so that everything expires immediately (note that if you're using Mono, that link wont help you much). However that isn't always the case. But thankfully, ASP.Net allows us to set that up directly on each individual page using OutputCache.

   1: <%@ OutputCache %>

Under that we can set the duration, vary by control, header, etc. So really it isn't that much of an issue when it comes to a page item.  Now the issue is how do we setup caching for IHTTPHandlers and things of that nature? It's actually rather simple. All we need to do is use the context.Response.Cache object in the ProcessRequest function. This object allows us to do exactly what the directive above does (cache an item). We can set the duration before it expires, we can set the ability to vary by a parameter, etc. For example:

   1: context.Response.Cache.SetLastModified(ModifiedDate);
   2: context.Response.Cache.SetExpires(DateTime.Now.ToUniversalTime().AddDays(7));
   3: context.Response.Cache.SetCacheability(HttpCacheability.Public);
   4: context.Response.Cache.SetMaxAge(new TimeSpan(7, 0, 0, 0));
   5: context.Response.Cache.SetRevalidation(HttpCacheRevalidation.None);
   6: context.Response.Cache.SetETag(ETag);

The code above sets this item's last modified date and says that it should expire in seven days. The item may be cached by anything really since it's set to public, also no revalidation is required (normally though, you'd probably want the item to revalidate). It also sets an ETag for the item. An ETag, by the way, is really just something to tell one object/entity from another (usually just a hash of something, like last date modified).

So when the response is sent to the client, this information is put into the header of the transmission, and the client knows it. However, clients might still send a request to the server every time it wants to use the information (depending on the browser, settings, etc.). So how can we check if the item has been updated on the server?

If-None-Match

Remember the ETag we gave the response previously? It gets sent back to us as If-None-Match in the header section (which can be reached by looking at context.Request.Headers["If-None-Match"]). If this equals the current ETag for the item on the server, then we know that it hasn't been changed since the last time the client was here. In this case, all we need to do is:

   1: context.Response.StatusCode = (int)HttpStatusCode.NotModified;
   2: context.Response.End();

Now normally speaking, this is usually enough. However some browsers don't set the If-None-Match item. Instead what they use is the item:

If-Modified-Since

Instead of sending the ETag, what they will do is send back the date the item was modified (or at least the last date that the client knows it was modified).  In this case, all you need to do is compare the date that the client sends and the last time the item was actually modified. If they are equal, nothing has changed. However if the item has been changed since the If-Modified-Since date, we need to send the new information. There is one thing to note though when comparing the dates, the If-Modified-Since date does not include anything lower than seconds. So if you take the last modified date of a file, they're not going to match up as that includes milliseconds. So you are going to have to round those off first.

Now this wont cache an item on every browser. They can always set it up so that the browser doesn't cache anything, in which case you're out of luck. But since not many people do that, you should be good to go. Anyway, try it out yourself, leave feedback, and happy coding.



Comments