IE6-8无法从HTTPS站点下载文件

时间:2012-10-29 10:00:01

标签: asp.net-mvc internet-explorer https mime-types

我有一个MVC .Net应用程序,其中包含返回报告文件的操作,通常是.xslx

byte[] data = GetReport();
return File(data, 
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 
    "filename.xlsx");

这在测试和所有浏览器中都很有用,但是当我们将它放在SSL网站上时,IE6,7和8(所有正确的浏览器仍能正常工作)都会失败,并且这个无用的错误:

Unable to download filename from server. Unable to open this Internet site. The requested site is either unavailable or cannot be found. Please try again later.

这曾经用于此操作替换的遗留应用程序(非MVC)。

我们无法告诉用户在本地更改任何内容 - 大约60%的用户仍在使用IE6!

如何使用MVC修复此问题?

更新

进一步挖掘表明这是IE6-8中的一个根本性失败。根据{{​​3}},这是因为在SSL连接期间,IE将no-cache指令视为绝对规则。因此,它不考虑缓存副本,而是将no-cache视为无法将副本保存到磁盘,即使 Content-Disposition:attachment并明确提示下载位置。

显然这是错误的,但是当它在IE9中得到修复时,我们仍然会遇到所有IE6-8用户。

使用MVC的动作过滤器属性会产生以下标题:

Cache-Control:no-cache, no-store, must-revalidate
Pragma:no-cache

使用Fiddler动态更改这些内容我们可以验证需要返回的标头:

Cache-Control:no-store, no-cache, must-revalidate

请注意Cache-Control 必须的顺序 no-store之前no-cache 以及Pragma指令< strong>必须完全删除。

这是一个问题 - 我们广泛使用MVC的动作属性,我真的不想从头开始重写它们。即使我们尝试删除Pragma指令,IIS也会抛出异常。

如何使Microsoft的MVC和IIS返回Microsoft的IE6-8可以在HTTPS下处理的无缓存指令?我不想允许私有缓存响应(根据此Eric Law's IE internals blog)或忽略MVC内置方法的覆盖(根据我自己的答案,这只是我目前最好的黑客攻击)。

2 个答案:

答案 0 :(得分:8)

我想出了一个解决方法,但这是一个明确的黑客 - 这是一个新的缓存属性来取代内置的[OutputCache]

[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);
    }
}

这是最好的解决方法 - 我真正想要的是扩展现有的[OutputCache]Response.Cache结构,以便它们具有适合旧版IE的所需输出。

答案 1 :(得分:2)

我有一个类似的方法,因为我有一个BaseController类

[OutputCache(Duration=0)]
public class BaseController : Controller
{
    //snip snip: some utility stuff and shared endpoints among all my controllers
}

这导致了IE8中的上述问题。然而,如上所示应用[IENoCacheAttribute]无效。问题是指令filterContext.HttpContext.Response.ClearHeaders()删除了我的所有标题,包括最终的Content-Disposition标题等...导致文件下载不正确。

因此,我的方法是以这样的方式覆盖默认的OutputCacheAttribute.cs:在IE的情况下,它不应用任何类型的缓存标头,尤其是有问题的no-cache标头。

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;
    }

}

以下是相应的要点:https://gist.github.com/4633225