Matthew
Matthew

Reputation: 4339

Why can't I set Cache-Control header in Web API2

To reproduce the issue: using Visual Studio 2015, create an Asp.Net framework web app with MVC and Web API. Create an Example api controller like this:

using System.Web;
using System.Web.Http;
public class ExampleController : ApiController
{
    public IHttpActionResult Get()
    {
        HttpContext.Current.Response.AppendHeader("Cache-Control", "no-cache, no-store, must-validate");
        return Ok("foo");
    }
}

And that's it. Run the app and check the dev tools in Chrome and the Cache-Control header is still just it's default value:

Cache-Control is set to default value instead of what was specified in Web Api controller method

If I change the above code to

HttpContext.Current.Response.AppendHeader("foobar", "no-cache, no-store, must-validate");

It actually does set the header:

It does add the foobar header

I can't find anything about this on google. I've tried the action filter attribute approach to setting the headers and it appears to be the exact same issue.

How do I override the default Cache-Control header in Asp.Net Web API?

Edit: I'm not sure what's wrong with the above, but if I replace with an action filter I can get it to work, except only if it's a synchronous controller method:

using System;
using System.Web.Http.Filters;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
public class CacheControlAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        if (context.Response != null)
        {
            context.Response.Headers.CacheControl = new CacheControlHeaderValue()
            {
                NoStore = true,
                NoCache = true,
                MustRevalidate = true,
                MaxAge = new TimeSpan(0)
            };
        }
        base.OnActionExecuted(context);
    }
    public override async Task OnActionExecutedAsync(HttpActionExecutedContext context, CancellationToken cancellationToken)
    {
        if (context.Response != null)
        {
            context.Response.Headers.CacheControl = new CacheControlHeaderValue()
            {
                NoStore = true,
                NoCache = true,
                MustRevalidate = true,
                MaxAge = new TimeSpan(0)
            };
        }
        await base.OnActionExecutedAsync(context, cancellationToken);
    }
}

That works with synchronous controller actions but not an async like this

    [CacheControl]
    public async Task<IHttpActionResult> Get()
    {
        using(Model1 db=new Model1())
        {
            var result =await db.MyEntities.Where(n => n.Name == "foo").SingleOrDefaultAsync();
        return Ok(result);
        }
    }

How do I get it to work with async?

Upvotes: 4

Views: 1884

Answers (2)

Devesh
Devesh

Reputation: 394

I think in your action method you are overrididng CacheControl headers by using OK("foo") after you set cache control headers, instead you should flip your code and set Cache Control afterwards.

Also, in you Action attribute

public override async Task OnActionExecutedAsync(HttpActionExecutedContext context, CancellationToken cancellationToken)
{
    await base.OnActionExecutedAsync(context, cancellationToken);
    if (context.Response != null)
    {
        context.Response.Headers.CacheControl = new CacheControlHeaderValue()
        {
            NoStore = true,
            NoCache = true,
            MustRevalidate = true,
            MaxAge = new TimeSpan(0)
        };
    }
}

Upvotes: 1

Csaba Toth
Csaba Toth

Reputation: 10697

Your CacheControlAttribute looks decent to me, not sure why it doesn't work. I was looking for less custom solutions, and what really helped me is enforcing the no cache directives by the RegisterGlobalFilters of App_Start/FilterConfig.cs:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // ...
        filters.Add(new OutputCacheAttribute
        {
            NoStore = true,
            Duration = 0,
            VaryByParam = "*",
            Location = System.Web.UI.OutputCacheLocation.None
        });
    }
}

Upvotes: 0

Related Questions