51

I'm trying to cache the output of an ApiController method in Web API.

Here's the controller code:

public class TestController : ApiController
{
    [OutputCache(Duration = 10, VaryByParam = "none", Location = OutputCacheLocation.Any)]
    public string Get()
    {
        return System.DateTime.Now.ToString();
    }
}

N.B. I'd also tried the OutputCache attribute on the controller itself, as well as several combinations of its parameters.

The route is registered in Global.asax:

namespace WebApiTest
{
    public class Global : HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.MapHttpRoute("default", routeTemplate: "{controller}");
        }
    }
}

I get a successful response, but it's not cached anywhere:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 18 Jul 2012 17:56:17 GMT
Content-Length: 96

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">18/07/2012 18:56:17</string>

I was not able to find documentation for output caching in Web API.

Is this a limitation of the Web API in MVC4 or am I doing something wrong?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Samu Lang
  • 2,261
  • 2
  • 16
  • 32

5 Answers5

46

WebAPI does not have any built in support for the [OutputCache] attribute. Take a look at this article to see how you could implement this feature yourself.

Liam
  • 27,717
  • 28
  • 128
  • 190
Cory
  • 12,404
  • 7
  • 33
  • 28
  • Tohid, I could not get that to work. Apart from anything else the HttpActionExecutedContext doesn't have the Result property. – Ben Power Apr 06 '15 at 00:57
  • 2
    what about this? http://www.strathweb.com/2012/05/output-caching-in-asp-net-web-api/ – Ali U Oct 07 '15 at 13:26
  • 1
    Flatwhite is a great output caching library with support for web api: https://github.com/vanthoainguyen/Flatwhite – Norrec Jun 13 '16 at 23:43
35

The answer of Aliostad states that Web API turns off caching, and the code of HttpControllerHandler shows that it does WHEN response.Headers.CacheControl is null.

To make your example ApiController Action return a cacheable result, you can:

using System.Net.Http;

public class TestController : ApiController
{
    public HttpResponseMessage Get()
    {
        var response = Request.CreateResponse(HttpStatusCode.OK);
        response.Content = new StringContent(System.DateTime.Now.ToString());
        response.Headers.CacheControl = new CacheControlHeaderValue();
        response.Headers.CacheControl.MaxAge = new TimeSpan(0, 10, 0);  // 10 min. or 600 sec.
        response.Headers.CacheControl.Public = true;
        return response;
    }
}

and you will get a HTTP response header like this:

Cache-Control: public, max-age=600
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Date: Wed, 13 Mar 2013 21:06:10 GMT
...
Nathan
  • 1,824
  • 1
  • 21
  • 34
Luciano Carvalho
  • 1,739
  • 15
  • 15
  • 8
    This question is about "output caching" which is a specific type of caching where the response is cached by the web server itself. AFAIK this is not related to Cache-Control headers.. – Tom Jan 31 '15 at 00:23
  • 2
    In my view, what you do on the server side, how you cache (memory, disk, database, other) and for how long is up to you. If you mark the item as cacheable and public, the browser and proxy servers (among others) on the way between the client and your server are allowed to serve themselves the cached content, instead of getting it from your server again. – Luciano Carvalho Feb 03 '15 at 18:23
  • 2
    @Tom The MVC output caching attribute parameters get translated to different Cache-Control headers. See this for an example: http://stackoverflow.com/a/20895704/1197771 So while this answer doesn't specifically use the `OutputCacheAttribute`, it does achieve the desired result. – RJ Cuthbertson Apr 17 '15 at 17:59
17

For the last few months, I have been working on HTTP caching for ASP.NET Web API. I have contributed to WebApiContrib for server-side and relevant information can be found on my blog.

Recently I have started to expand the work and add the client-side as well in the CacheCow library. First NuGet packages have been released now (thanks to Tugberk) More to come. I will write a blog post soon on this. So watch the space.


But in order to answer your question, ASP.NET Web API by default turns off the caching. If you want the response to be cached, you need to add the CacheControl header to the response in your controller (and in fact better be in a delegating handler similar to CachingHandler in CacheCow).

This snippet is from HttpControllerHandler in ASP.NET Web Stack source code:

        CacheControlHeaderValue cacheControl = response.Headers.CacheControl;

        // TODO 335085: Consider this when coming up with our caching story
        if (cacheControl == null)
        {
            // DevDiv2 #332323. ASP.NET by default always emits a cache-control: private header.
            // However, we don't want requests to be cached by default.
            // If nobody set an explicit CacheControl then explicitly set to no-cache to override the
            // default behavior. This will cause the following response headers to be emitted:
            //     Cache-Control: no-cache
            //     Pragma: no-cache
            //     Expires: -1
            httpContextBase.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        }
Community
  • 1
  • 1
Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • This link doesn't work: http://byterot.blogspot.co.uk/2012/07/cachecow-http-caching-framework-server.html – Tohid Nov 02 '12 at 13:12
  • 1
    @Tohid have a look here http://byterot.blogspot.co.uk/2012/07/introducing-cachecow-http-caching.html – Aliostad Nov 03 '12 at 22:02
  • @Aliostad do you have a link to documentation supporting the statement "ASP.NET Web API by default turns off the caching"? I know this is true - just need official confirmation. – Zephyr was a Friend of Mine Feb 04 '13 at 21:28
  • @NoelAbrahams studying the ASP.NET Web API codebase my friend :) Is not documented as far I am aware. – Aliostad Feb 04 '13 at 21:30
7

I am very late, but still thought to post this great article on Caching in WebApi

https://codewala.net/2015/05/25/outputcache-doesnt-work-with-web-api-why-a-solution/

public class CacheWebApiAttribute : ActionFilterAttribute
{
    public int Duration { get; set; }

    public override void OnActionExecuted(HttpActionExecutedContext filterContext)
    {
        filterContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
        {
            MaxAge = TimeSpan.FromMinutes(Duration),
            MustRevalidate = true,
            Private = true
        };
    }
}

In the above code, we have overridden OnActionExecuted method and set the required header in the response. Now I have decorated the Web API call as

[CacheWebApi(Duration = 20)]
        public IEnumerable<string> Get()
        {
            return new string[] { DateTime.Now.ToLongTimeString(), DateTime.UtcNow.ToLongTimeString() };
        }
himanshupareek66
  • 766
  • 10
  • 22
-23

You could use this on a regular MVC Controller:

[OutputCache(Duration = 10, VaryByParam = "none", Location = OutputCacheLocation.Any)]
public string Get()
{
    HttpContext.Current.Response.Cache.SetOmitVaryStar(true);
    return System.DateTime.Now.ToString();
}

but OutputCache attribute is in System.Web.Mvc namespace and not available in an ApiController.

Luciano Carvalho
  • 1,739
  • 15
  • 15
broxten
  • 5
  • 6