Recently I encountered a bug that only some users saw, and which did not reproduce locally on development environment. The setup was:
- An intranet page has an IFrame
- …that is dynamically changed to point to an attachment
- …which is served from MVC action returning FileContentResult
This is a pretty common pattern to open files on browser without leaving the current page. And it worked like charm in all browsers, except on Internet Explorer 8 on testing environment, where IE just showed an error message that the address cannot be opened. IE 7 might be affected too, but I did not test on that.
To prevent caching pages I had a hand made MVC no cache action filter attribute (NoBrowserCacheAttribute) which is registered globally and did this:
// Set all the various headers that control caching var cache = filterContext.HttpContext.Response.Cache; cache.SetExpires(DateTime.UtcNow.AddDays(-1)); cache.SetValidUntilExpires(false); cache.SetRevalidation(HttpCacheRevalidation.AllCaches); cache.SetCacheability(HttpCacheability.NoCache); cache.SetNoStore();
But IE does not like the Pragma: no-cache header or the Expires: -1 header that yield from the above settings. Here are the settings that fixed the IE issue for me (used these instead of the above settings):
var response = filterContext.HttpContext.Response; response.Cache.SetExpires(DateTime.UtcNow); response.CacheControl = "private";
There are a couple of ways to use these less offensive cache settings per action method basis:
- Allow multiple declarations of the NoBrowserCacheAttribute, and declare it again with some different properties on the file outputting action methods
- Create a new action filter that is used only on file outputting action methods
- Create new file content class that changes HTTP headers
- Signal the no cache attribute that it should be less offensive
The last one was what I ended up doing: I created a static method SetSafeAttachmentCache() to the attribute, and called that from affected action methods; not very pedant but easy and works. The information (bit) was then stored in HttpContext.Items collection, and read from there at the time of writing HTTP headers.