SetLastModified ignored when using an OutputCacheAttribute

741 Views Asked by At

I've got an ASP.NET MVC method (v3.0 on .NET 4.0) set up like the following:

[OutputCache(Duration = 31536000, Location = OutputCacheLocation.Any)]
public virtual ActionResult Item()
{
    this.Response.Cache.SetLastModified(new DateTime(2011, 01, 01));
    return this.Content("hello world", "text/plain");
}

I'd expect this to come back with the Last-Modified header set to Mon, 07 Feb 2011 00:00:00 GMT as specified, however it is actually coming back as the date that the output was first cached in the output cache (i.e. the first time the method was called since IIS was reset).

If I comment out the [OutputCache] attribute so that no output caching is done then the Last-Modified header comes back as expected, so it appears that it's something in the output caching infrastructure that's choosing to ignore my specified value for this.

Any idea why it might be doing so? And is there any way to make it use my specified value as the Last-Modified date?

2

There are 2 best solutions below

1
On BEST ANSWER

Well, I never did work out the reason this occurs, but it looks like a bug somewhere in the ASP.NET page caching infrastructure which the [OutputCache] attribute uses.

I ended up writing a custom [HttpCache] attribute with much the same public interface, but which calls the appropriate cache methods on the Response.Cache object directly rather than delegating to the ASP.NET page caching infrastructure.

That works fine. It's a shame the built-in attribute doesn't.

0
On

During the Controller's OnResultExecuting event, [OutputCache] creates an instance of System.Web.UI.Page to handle the cache properties specified in the attribute. They do this because Page already has logic to translate OutputCacheParameters to actual http cache directives.

https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/OutputCacheAttribute.cs

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if (!filterContext.IsChildAction)
        {
            // we need to call ProcessRequest() since there's no other way to set the Page.Response intrinsic
            using (OutputCachedPage page = new OutputCachedPage(_cacheSettings))
            {
                page.ProcessRequest(HttpContext.Current);
            }
        }
    }

The OutputCacheAttribute basically copies the output from the original handler (controller) to the Page created to configure the cache.

The disadvantage here is that headers added to the original HttpResponse do not get copied over to the new handler (Page). This means it's impossible to set headers on the Response in the controller. The Page that actually processes the request ignores these headers.