为ApiController(MVC4 Web API)启用输出缓存

51

我正在尝试在Web API中缓存一个ApiController方法的输出。

这是控制器代码:

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

注意:我也尝试过在控制器本身使用OutputCache属性,以及其参数的多种组合。

该路由在Global.asax中注册:

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

我得到了一个成功的响应,但它没有被缓存任何地方:

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>

我无法找到关于Web API输出缓存的文档。

这是MVC4中Web API的限制还是我的操作有误?

5个回答

46

WebAPI没有内置支持[OutputCache]属性。请参阅此文章了解如何自行实现此功能。


Tohid,我无法让其工作。除此之外,HttpActionExecutedContext中没有Result属性。 - Ben Power
2
这个怎么样?http://www.strathweb.com/2012/05/output-caching-in-asp-net-web-api/ - Ali U
1
Flatwhite是一个出色的输出缓存库,支持Web API:https://github.com/vanthoainguyen/Flatwhite - Norrec

35

Aliostad的回答指出Web API会关闭缓存,而HttpControllerHandler的代码表明,当response.Headers.CacheControl为空时,它会这样做。

要使您的示例ApiController操作返回可缓存的结果,您可以:

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;
    }
}

然后您将获得一个如下的HTTP响应头:

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
...

8
这个问题是关于“输出缓存”的,它是一种特定类型的缓存,其中响应由 Web 服务器本身进行缓存。据我所知,这与 Cache-Control 标头无关。 - Tom
2
在我看来,服务器端的操作、缓存方式(内存、磁盘、数据库、其他)以及缓存时间长度都由你自己决定。如果你将项目标记为可缓存和公共的,那么在客户端和你的服务器之间的浏览器和代理服务器等可以自行提供缓存内容,而不必再从你的服务器获取。 - Luciano Carvalho
2
@Tom,MVC输出缓存属性参数会被转换为不同的Cache-Control标头。请参见此示例:https://dev59.com/wWEi5IYBdhLWcg3w2fQm#20895704。因此,即使此答案没有专门使用`OutputCacheAttribute`,它也能实现所需的结果。 - RJ Cuthbertson

17
在过去的几个月中,我一直在为ASP.NET Web API开发HTTP缓存。我为服务器端做出了WebApiContrib贡献,相关信息可以在我的博客上找到。
最近,我开始扩展工作,并在CacheCow库中添加客户端。现在已经发布了第一个NuGet包(感谢Tugberk)。还有更多的内容即将推出。我会很快在博客上发布一篇文章介绍这方面的内容。请关注。
为了回答你的问题,ASP.NET Web API默认关闭缓存。如果你想要响应被缓存,你需要在控制器中添加CacheControl头部到响应中(最好是在类似于CacheCow中的委托处理程序中)。
这段代码片段来自于ASP.NET Web Stack源代码中的HttpControllerHandler。
        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);
        }

这个链接无法打开:http://byterot.blogspot.co.uk/2012/07/cachecow-http-caching-framework-server.html - Tohid
1
@Tohid,请看这里:http://byterot.blogspot.co.uk/2012/07/introducing-cachecow-http-caching.html - Aliostad
@Aliostad,你有支持“ASP.NET Web API默认关闭缓存”的声明的文档链接吗?我知道这是真的 - 只需要官方确认。 - Zephyr was a Friend of Mine
@NoelAbrahams 正在研究 ASP.NET Web API 代码库,我的朋友 :) 据我所知,它没有被记录文档。 - Aliostad

7
我很晚才来,但仍然想发布这篇关于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
        };
    }
}

在上面的代码中,我们重写了OnActionExecuted方法并在响应中设置了所需的标题。现在我已经将Web API调用装饰为:
[CacheWebApi(Duration = 20)]
        public IEnumerable<string> Get()
        {
            return new string[] { DateTime.Now.ToLongTimeString(), DateTime.UtcNow.ToLongTimeString() };
        }

-23
您可以在常规的MVC控制器上使用此功能:
[OutputCache(Duration = 10, VaryByParam = "none", Location = OutputCacheLocation.Any)]
public string Get()
{
    HttpContext.Current.Response.Cache.SetOmitVaryStar(true);
    return System.DateTime.Now.ToString();
}

但 OutputCache 属性位于 System.Web.Mvc 命名空间中,因此在 ApiController 中不可用。


1
你正在使用的 [OutputCache] 来自 System.Web.Mvc,而不是 System.Web.Http (WebApi)。 - Korayem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接