I have an MVC .Net application that has actions that return report files, usually .xslx
:
byte[] data = GetReport();
return File(data,
\"applica
I was having a similar approach in that I had a BaseController class
[OutputCache(Duration=0)]
public class BaseController : Controller
{
//snip snip: some utility stuff and shared endpoints among all my controllers
}
That caused the above mentioned problems in IE8. Applying the [IENoCacheAttribute]
as shown above didn't work however. The problem is that the instruction filterContext.HttpContext.Response.ClearHeaders()
removes all of my headers including eventual Content-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 problematic no-cache
ones.
public class EnhancedOutputCacheAttribute : OutputCacheAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (!IsFileResultAndOldIE(filterContext))
base.OnActionExecuted(filterContext);
else
{
//try the best to avoid any kind of caching
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Private);
filterContext.HttpContext.Response.Cache.SetMaxAge(new TimeSpan(0));
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.Now.AddMinutes(-5D));
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsFileResultAndOldIE(filterContext))
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (!IsFileResultAndOldIE(filterContext))
base.OnResultExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (!IsFileResultAndOldIE(filterContext))
base.OnResultExecuting(filterContext);
}
/// <summary>
///
/// </summary>
/// <param name="filterContext"></param>
/// <returns><c>true</c> for FileResults and if the browser is < IE9</returns>
private bool IsFileResultAndOldIE(dynamic filterContext)
{
return filterContext.Result is FileResult &&
filterContext.HttpContext.Request.IsSecureConnection &&
string.Equals(filterContext.HttpContext.Request.Browser.Browser, "IE", StringComparison.OrdinalIgnoreCase) &&
filterContext.HttpContext.Request.Browser.MajorVersion < 9;
}
}
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:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class IENoCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.IsSecureConnection &&
string.Equals(filterContext.HttpContext.Request.Browser.Browser, "IE", StringComparison.OrdinalIgnoreCase) &&
filterContext.HttpContext.Request.Browser.MajorVersion < 9)
{
filterContext.HttpContext.Response.ClearHeaders();
filterContext.HttpContext.Response.AddHeader("cache-control", "no-store, no-cache, must-revalidate");
}
else
{
filterContext.HttpContext.Response.Cache.SetNoStore();
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
}
base.OnResultExecuting(filterContext);
}
}
It's a workaround at best though - what I really want is to extend the existing [OutputCache]
and Response.Cache
structures so that they have the desired output suitable for legacy IEs.