web-dev-qa-db-ja.com

ApiControllerの出力キャッシュ(MVC4 Web API)

Web APIで ApiController メソッドの出力をキャッシュしようとしています。

コントローラコードは次のとおりです。

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

N.B.また、コントローラー自体の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の制限ですか、それとも何か間違っていますか?

46
Samu Lang

WebAPIには、[OutputCache]属性のサポートが組み込まれていません。 この記事をご覧ください を使用して、この機能を自分で実装する方法を確認してください。

40
Cory

Aliostadの答えは、Web APIがキャッシュをオフにし、HttpControllerHandlerのコードが、それがWHEN response.Headers.CacheControlがnullであることを示していることを示しています。

サンプルの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
...
33

過去数か月間、私はASP.NET Web APIのHTTPキャッシングに取り組んできました。私は WebApiContrib に貢献しました。サーバー側で、関連情報は blog にあります。

最近、作業を拡張し、クライアント側も CacheCow ライブラリに追加し始めました。最初のNuGetパッケージがリリースされました( Tugberk に感謝します)これについてはすぐにブログ記事を書きます。だからスペースを見てください。


ただし、質問に答えるために、ASP.NET Web APIはデフォルトでキャッシュをオフにします。応答をキャッシュする場合は、コントローラーの応答にCacheControlヘッダーを追加する必要があります(実際、CacheCowのCachingHandlerに似た委任ハンドラーを使用する方が適切です)。

このスニペットは、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);
        }
15
Aliostad

私は非常に遅れていますが、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() };
        }
2