I have an MVC .Net application that has actions that return report files, usually .xslx
:
byte[] data = GetReport();
return File(data,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"filename.xlsx");
This works great in testing and in all browsers, but when we put this on an SSL site it fails for IE6, 7 and 8 (all the proper browsers still work fine) with this unhelpful error:
This used to work in a legacy application (non-MVC) that this action replaces.
We can't tell our users to change anything locally - about 60% are still on IE6!
How can I fix this using MVC?
Update
Further digging reveals that this is a fundamental failure in IE6-8. According to Eric Law's IE internals blog this happens because, during an SSL connection, IE treats the no-cache directive as an absolute rule. So, rather than not cache a copy, it considers no-cache to mean that it shouldn't be possible to save a copy to disk even when
Content-Disposition:attachment
and with an explicit prompt for a download location.
Obviously this is wrong, but while it's fixed in IE9 we're still stuck with all the IE6-8 users.
Using MVC's action filter attributes produces the following headers:
Cache-Control:no-cache, no-store, must-revalidate
Pragma:no-cache
Using Fiddler to change these on the fly we can verify the headers that need to be returned instead:
Cache-Control:no-store, no-cache, must-revalidate
Note the order of the Cache-Control
must have no-store
before no-cache
and that the Pragma
directive must be removed entirely.
This is a problem - we use MVC's action attributes extensively and I really don't want to rewrite them from scratch. Even if we could IIS throws an exception if you try to remove the Pragma
directive.
How do you make Microsoft's MVC and IIS return the no-cache directive that Microsoft's IE6-8 can handle under HTTPS? I do not want to allow private caching of the response (as per this similar question) or ignore the MVC built in methods with an override (as per my own answer, which is just my current best hack).
I was having a similar approach in that I had a BaseController class
That caused the above mentioned problems in IE8. Applying the
[IENoCacheAttribute]
as shown above didn't work however. The problem is that the instructionfilterContext.HttpContext.Response.ClearHeaders()
removes all of my headers including eventualContent-Disposition
headers etc... causing the file download to not happen correctly.My approach was therefore to overwrite the default
OutputCacheAttribute.cs
in such a way that in case of IE it didn't apply any kind of caching headers, especially the problematicno-cache
ones.Here's the corresponding gist: https://gist.github.com/4633225
I've come up with a workaround, but it's a definite hack - this is a new cache attribute to replace the built-in
[OutputCache]
one:It's a workaround at best though - what I really want is to extend the existing
[OutputCache]
andResponse.Cache
structures so that they have the desired output suitable for legacy IEs.