2

No matter what I do I can't get images returned from a web api too cache, I am seeing a request to the server every time I request the image.

I have tried many permiations for the header setting, here is my latest effort:

  public class TestController : ApiController
    {
        public HttpResponseMessage Get(int id)
        {
            HttpResponseMessage response = new HttpResponseMessage();

            StreamContent streamContent = new StreamContent(File.Open("c:\\1.jpg", FileMode.Open, FileAccess.Read, FileShare.Read));
                    
            response.Content = streamContent;
            response.Content.Headers.Add("Content-Type", "image/jpeg");
            response.Headers.CacheControl = new CacheControlHeaderValue
            {
                MustRevalidate = true,
                Private= false,
                MaxAge = TimeSpan.FromMinutes(1),
            };

            //string hash = new Guid("524DF956-D67A-4D66-A3E0-5E78726A204A").GetHashCode().ToString();
            //response.Headers.ETag = new EntityTagHeaderValue(String.Concat("\"", hash, "\""), true);
            //response.Content.Headers.LastModified = new DateTimeOffset(new DateTime(2012, 12, 24));
            //response.Content.Headers.Expires = new DateTimeOffset(new DateTime(2013, 12, 24));
            return response;
        }

And this is what the response headers look like when I inspect with chrome:

Cache-Control:must-revalidate, max-age=60 Content-Length:24233

Content-Type:image/jpeg Date:Sun, 14 Apr 2013 22:04:32 GMT

Server:Microsoft-IIS/8.0 X-AspNet-Version:4.0.30319

X-Powered-By:ASP.NET

X-SourceFiles:=?UTF-8?B?Qzpcc291cmNlXFRydW5rXFRlc3RcYXBpXHRlc3RcMQ==?=

Any ideas on how to proceed?

Community
  • 1
  • 1
Dan
  • 29,100
  • 43
  • 148
  • 207

2 Answers2

2

If you're looking for a 200 / 304 situation, then probably the biggest step you're missing at this stage, is that your server code needs to choose when to return a 304, vs a 200 with full message body. When the conditions of your cache rules are met (and you'll need to decide what those are), then you'll want:

return new HttpResponseMessage(HttpStatusCode.NotModified); // 304

Part2:

The next choice then, is deciding on your cache rule. Your current code won't get any further than the first step of cache check (if even that). You need to return a LastModified and / or ETag for the client to then use in subsequent requests to perform a cache check.

LastModified is fairly straight-forward. Compare the dates, if the resource has been modified since the LastModified date, then return a full response, othrwise, 304.

ETag should uniquely represent the resource, not the request (as your current commented out ETag code would be doing). A hash may make sense for an ETag, but only if it's a hash of the resource you're returning, not if it's a completely unique hash each time. Think of it like a timestamp, or version number even. Client gets version 1 of an image, with an ETag value of (hypothetically) "1". Client then sends a request for that image including the ETag value, the server then checks if "1" is still the valid version and returns 200 + body + new ETag or 304 as appropriate.

Snixtor
  • 4,239
  • 2
  • 31
  • 54
  • Thanks for taking the time to answer. Having to manually build the 304 response doesn't seem right though, the reason I wanted to do this was to reduce requests to the server, If I have sent the image out with the right headers surely the browser shouldn't even be making another request until the expiry period has passed? Am I fundamentally miss understanding this. – Dan Apr 15 '13 at 06:35
  • Ah, well a 304 is a response to a "revalidate" request. You need to adjust the cache control appropriately for client-side caching without revalidation. Setting `MustRevalidate` to false and the `MaxAge` to a higher value would be your first step. – Snixtor Apr 15 '13 at 06:53
  • There's also circumstances where a browser will *always* re-validate. On an image for example, specifying it via `src` on an `img` element will exhibit one behaviour, but typing the URL directly into the address bar may elicit a different action. – Snixtor Apr 15 '13 at 06:55
0

You should put Private=true if you want the browser to be able to cache your content and public=true if you want shared caches to cache it.

Darrel Miller
  • 139,164
  • 32
  • 194
  • 243