4

I'm developing a Web API RESTful service on the Azure platform.

I thought that the default client-side caching behavior would be to cache GET requests (since GET is idempotent and all).

To my surprise when I deployed the service to Azure all responses were sent with a Cache-Control: private header or other cache-disallowing header.

I tried the solution suggested in this question, it did work locally in IIS but did not work once we deployed to Azure. I could not find anything in the documentation about this ability which I thought was very basic in a RESTful service, I really hope that I'm missing something obvious, in MVC it was very easy.

tl;dr

We need to cache GET requests on the client side when using Azure and Web API.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Can you give more information, like are you deploying on Windows Azure Website, or a Cloud Service? – sabbour Feb 14 '13 at 16:47
  • @sabbour I'm pretty sure we're running as a Windows Azure Website (deployed using Web Deploy) we can deploy under what configuration that helps though. – Benjamin Gruenbaum Feb 14 '13 at 18:40
  • 1
    Are you actually setting any caching properties of your HttpWebResponse? Can you show some sample code that explains exactly what you've done to try to get caching to work? The question you linked to is about leveraging ASP.NET Output Caching, but that's a whole other beast than "simple" HTTP request caching. – Drew Marsh Feb 14 '13 at 22:31
  • @DrewMarsh you're correct, I linked to the wrong question, I'll look up the correct one, thank you. I'm just looking for a way to set the Cache-Control header – Benjamin Gruenbaum Feb 14 '13 at 22:41
  • For posterity, I typo'd when I said HttpWebResponse... obviously that's the older HTTP API. I meant HttpResponseMessage of course. See my answer below. – Drew Marsh Feb 14 '13 at 23:06
  • This was in-fact a WebService and not a WebSite deployment, if that matters to anyone – Benjamin Gruenbaum Mar 19 '13 at 00:55
  • Also worth mentioning, deploying as web-side makes life a lot easier. – Benjamin Gruenbaum Jun 10 '13 at 00:13
  • See Jacob's answer on https://stackoverflow.com/questions/15911356/setting-http-cache-control-headers-in-webapi/22620144#22620144 – LiQuick.net Sep 10 '14 at 10:01

3 Answers3

5

I don't believe Azure is doing anything to you in this respect. It's a matter of you needing to specify exactly what caching properties you want for your resource(s).

With WebAPI you can control what caching properties your response has via the CacheControlHeaderValue which is accessible via the myHttpResponseMessage.Headers.CacheControl property.

Assuming you had a controller action like this:

public Foo Get(int id)
{
    Foo myFoo = LoadSomeFooById(id);

    return myFoo;
}

You'll need to do something like this to take control of the caching explicitly:

public HttpResponseMessage Get(int id)
{
    Foo myFoo = LoadSomeFooById(id);

    HttpResponseMessage myHttpResponseMessage = this.Request.CreateResponse(HttpStatusCode.OK, myFoo)

    CacheControlHeaderValue cacheControlHeaderValue = new CacheControlHeaderValue(); 
    cacheControlHeaderValue.Public = true;    
    cacheControlHeaderValue.MaxAge = TimeSpan.FromMinutes(30);

    myHttpResponseMessage.Headers.CacheControl = cacheControlHeaderValue;

    return myHttpResponseMessage;
}

Many of the other properties related to caching that you'd expect are also available on the CacheControlHeaderValue class, this is just the most basic example.

Also, bear in mind my example is extremely brute force/simplistic in that all the caching behavior/logic is right there in the action method. A much cleaner implementation might be to have an ActionFilterAttribute which contains all the caching logic based on attribute settings and applies it to the HttpResponseMessage. Then you could revert to the more model centric action method signature because you would, in this case, no longer need access to the HttpResponseMessage anymore at that level. As usual, many ways to skin the cat and you have to determine which works best for your specific problem domain.

Drew Marsh
  • 33,111
  • 3
  • 82
  • 100
  • Thanks a lot, Where _is_ myHttpResponseMessage? (For example in a web api controller Get action) – Benjamin Gruenbaum Feb 14 '13 at 23:22
  • 2
    If you're were not previously returning an HttpResponseMessage explicitly (e.g. you were returning a strongly typed domain object and letting WebAPI wrap that for you) you will need to now. Once you want to take control of HTTP characteristics like this, you need to start working at that level. I will update the sample code with a more explicit example. – Drew Marsh Feb 15 '13 at 00:08
  • Drew thank you for your response. That looks pretty horrifying (having to return an HttpResponseMessage). Areyou sure there is no other way to ask my application to cache GET requests on the client side by default? (ActionFilterAttribute is what I'm tending towards, but than what advantage do I get over an ASP.NET MVC controller) – Benjamin Gruenbaum Feb 16 '13 at 14:35
  • 1
    I am quite sure. Once you want to act on the HTTP level details, you must work with Http[Request|Response]Message themselves. Keeping you in direct control of HTTP was one of the core tenets of the design of WebAPI and this is how they do it. The "simplest" model is just doing it in the controller itself, but you can start to abstract this stuff away from your controller actions via the various extensibility points such as formatters, model binders, action filters, etc. Ultimately though, something in the stack is going to be mapping to/from Http[Request|Response]Message. – Drew Marsh Feb 18 '13 at 02:16
0

Take a look at this http://forums.asp.net/post/4939481.aspx it implements caching as an attribute that modifies the HTTP response.

Disclaimer: I haven't tried it.

sabbour
  • 4,969
  • 6
  • 34
  • 33
0

I would recommend this https://github.com/filipw/AspNetWebApi-OutputCache

Simple, quick and has various options to cache.

Hope that helps

Pooran
  • 1,640
  • 2
  • 18
  • 25