OutputCache doesn’t work with Web API – Why? A solution

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

Output Cache is one of the most useful features of ASP.NET and plays a key role for making high performance web applications. It’s very easy to use this feature. Just put OutputCache attribute on any controller/action or you can setoutputCacheSettings attribute in web.config. ASP.NET MVC and ASP.NET Web API are two technologies that looks very similar when we work with them. They have similar concepts like Controller, Action, routing etc and work almost in similar way except one returns View and returns data in JSON/XML format.

Recently in an application, I was required to cache some web api calls so I put the OutputCache attribute over the Web API action but when I ran it was not working. The data was not getting cached and on every request, it was going to server and further fetching data from database and returning the same. Then to verify the attribute, I put the same attribute over a MVC action and it was working like a charm. So I felt that it looks like that this attribute is not working.

It made me bit irritated and I was expecting that it should give an error if it is not supported then. I had an impression that all the features available with MVC also works with Web API and vice versa. Let’s have a look with an example

1- I have created a TestController (MVC) and Index View.

2- Then I am assigning current time in ViewBag and displaying in the view.

3- Then I decorated the Action with OutputCache attribute. Let’s see the code

public class TestController : Controller
{
    [OutputCache(Duration = 20, VaryByParam = "none", Location = OutputCacheLocation.ServerAndClient)]
    public ActionResult Index()
    {
        ViewBag.Message = "Message set at server at " + System.DateTime.Now.ToLongTimeString();
        return View();
    }
}

We saw the TestController above. Let’s look at Index View

@{
    ViewBag.Title = "View";
}
<h2>My MVC View</h2>
@ViewBag.Message

It’s simple.Now lets run it

cachedmvc

As here we can see that the time is not getting updated after multiple refreshes. This is very expected behavior that we want from the attribute.

Now let’s See the Web Api part.
1- I created a TestDataController which inherits from ApiController.
2- It has a Get method which return a list of string. Here for simplicity, it contains two items- DateTime in normal and UTC format.
3- Here I also decorated with OutputCache attribute to the Web API method as I did in MVC example.
4- Then I created another MVC action (Details) in TestController and created a View accordingly.
5- Now from the Details view, I am calling the web api via jQuery Ajax.

Let’s quickly have a look at the code

public ActionResult Details()
{
    return View();
}

  

 

Web API Controller :

public class TestDataController : ApiController
{
    [OutputCache(Duration = 20, VaryByParam = "none", Location = OutputCacheLocation.ServerAndClient)]
    public IEnumerable<string> Get()
    {
        return new string[] { DateTime.Now.ToLongTimeString(), DateTime.UtcNow.ToLongTimeString() };
    }
}

  

 

Above controller has the same OutputCache attribute.

MVC View:

<h2>Details</h2>
<input type="button" value="Call Api" onclick="LoadData();"/> 
<div id="dvapidata"></div>
 
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script>
    LoadData();
    function LoadData() {
        $.ajax({
            type: "Get",
            url: "/api/TestData",
            contentType: "application/json; charset=utf-8",
            data: "{}",
            dataType: "json",
            success: AjaxSucceeded,
            error: AjaxFailed
        });
 
    }
    function AjaxSucceeded(result) {
        $('#dvapidata').html(result);
    }
    function AjaxFailed(result) {
        alert('no success');
    }
</script>

  

 

Now let’s run that and have a look

webapinocache

 

Oh.. here the data is not cached and time is getting updated in every call which is not expected.

Let’s analyze why it did not work for Web API

To get the details, I tried to dig it further to see details of the HTTP request and response for both (MVC and Web API). Let’s have a look that I collected via fiddler

ResponseHeadersSo clearly we can see in the left side (Red encircled area) that Cache header is set properly which is response header for MVC action, while in the right side, we see that (Blue encircled area), Cache control is set as no-cache. It means OutputCacheattribute is not setting the proper cache header in response for web api.

To solve this we can created our own custom filter the adds the appropriate header at web api call as we saw above so that it can be cached at client and then decorate that attribute at Web  API call. Lets create that

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

  

 

Now let’s run the application.

CachedwebApiIt is working as expected and the response is getting cached. Lets see the response header via fiddler now

WebApiHere see the blue encircled area, cache header is set. So this is the reason response is getting cached now.

Hope you have enjoyed the post and it has helped you.

Cheers
Brij

posted @ 2018-11-08 10:41  Zeroes  阅读(203)  评论(0)    收藏  举报