I'm working on a web app in ASP.NET 2.0 that involves serving images via a resource handler (.ashx). I've just implemented handling cache headers and conditional GET requests, so that I don't have to serve all the images for every request. But I'm not sure I'm completely understanding what's happening with the browser's cache.
Images are fetched via urls like http://www.mysite.com/image.ashx?imageID=3
. My code in the handler looks something like this:
int imageID = -1;
try
{
imageID = Int32.Parse(context.Request["imageID"]);
}
catch (Exception) {}
MyImageClass image = DataLayer.GetImage(imageID);
if (image != null)
{
DateTime requestedDate = DateTime.MinValue;
if (context.Request.Headers["If-Modified-Since"] != null)
{
requestedDate = DateTime.Parse(context.Request.Headers["If-Modified-Since"])
.ToLocalTime();
}
if (requestedDate < image.ModifiedDate)
{
context.Response.AddHeader("content-type", image.ContentType);
context.Response.CacheControl = HttpCacheability.Private.ToString();
context.Response.Cache.SetLastModified(image.ModifiedDate.ToUniversalTime());
context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1));
//write image to output stream
}
else
{
context.Response.StatusDescription = "Not Modified";
context.Response.StatusCode = 304;
}
}
This is what the response header looks like the first time an image is requested:
HTTP/1.1 200 OK
Cache-Control: private, max-age=86400
Content-Length: 1048576
Content-Type: image/jpeg
Expires: Sat, 28 Jan 2012 17:17:11 GMT
Last-Modified: Fri, 27 Jan 2012 16:50:27 GMT
Server: Microsoft-IIS/7.5
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Fri, 27 Jan 2012 17:17:10 GMT
And this is a response to a subsequent request:
HTTP/1.1 304 Not Modified
Cache-Control: private
Server: Microsoft-IIS/7.5
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Fri, 27 Jan 2012 17:17:30 GMT
Connection: close
Watching the requests in Fiddler, I'm noticing that the browser (Firefox 9) always makes a conditional GET request for the image after the first request. It gets the 304 Not Modified
response and pulls the image from cache, which is great. But isn't there a way to make it always pull from the cache, without even asking the server, until after the header's max-age (or expiry date) is past? I've tried using context.Response.Cache.SetExpires()
with a future date, and the browser still makes the conditional GET request.
When you press F5 or Reload, Firefox will always send conditional requests.
If you navigate to the page normally (eg, clicking a link or using the address bar), it will go straight to the cache.
No HTTP header guarantees any caching behavior.
The
Expires
header indicates the client the date before which all queries to the same URI are meaningless. But any HTTP client may obey it and may not.Also, when you hit F5 or Reload or Refresh, nearly any browser requeries all the page assets.
Static resource handler looks very simple at the surface but to be honest it is not for the faint hearted - not implying that you are one, I just mean that it is a lot more work than those few lines.
You have to take into account all the possibilities HTTP provides. One concept missing in yours is the e-tag.
You have to accomodate all these HTTP request headers in addition to the expires, etc:
I would just leave it to the framework to do it.I realise you have to do all this yourself. I would have a look at an implementation of static file handler and how it has implemented all this.
UPDATE
Have a look at this sample.