为什么手动刷新响应时,ASP.NET会用Transfer-Encoding标头替换Content-Length标头?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (43)

我们的Web应用程序(ASP.NET Web窗体)有一个页面,它将向用户显示最近生成的PDF文件。由于PDF文件有时非常大,我们实施了一种“流式传输”方法,将其以块的形式发送到客户端浏览器。

尽管以块的形式发送数据,但我们在发送文件之前知道文件的全部大小,所以我们适当地设置了Content-Length头。这一直在我们的生产环境中工作一段时间(并继续在我们的测试环境中以几乎相同的配置工作)直到今天。报道的问题是,Chrome会尝试打开PDF文件,但会挂起“加载”动画卡住。

因为在我们的测试环境中,一切仍然正常,所以我可以使用Firebug来查看两种环境中都返回的响应头。在测试环境中,我看到一个正确的'Content-Length'标题,而在生产中已被一个Transfer-Encoding:分块标题取代。Chrome不喜欢这个,因此挂断电话。

我读过一些文章和文章,谈论如何在没有提供Content-Length头时显示Transfer-Encoding头,但是我们指定Content-Length头并且在运行相同的代码测试服务器上的相同PDF文件。

测试和生产服务器都运行IIS 7.5,并且都启用了动态和静态压缩。

这里是有问题的代码:

var fileInfo = new FileInfo(fileToSendDown);
Response.ClearHeaders();
Response.ContentType = "application/pdf";            
Response.AddHeader("Content-Disposition", "filename=test.pdf");
Response.AddHeader("Content-Length", fileInfo.Length.ToString());
var buffer = new byte[1024];
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    int read;
    while ((read = fs.Read(buffer, 0, 1024)) > 0)
    {
        if (!response.IsClientConnected) break;
        Response.OutputStream.Write(buffer, 0, read);
        Response.Flush();
    }
}

我很幸运地在我的本地工作站上看到相同的行为,所以使用调试器,我已经能够看到在调用'Flush'期间第二遍while循环中设置了'Transfer-Encoding:chunked' 。此时,响应同时包含Content-Length头和Transfer-Encoding头,但不知何故,当响应到达浏览器时,Firebug只显示Transfer-Encoding头。

我认为我已经跟踪了这一点,使用了以“块”方式发送数据并将“过滤器”附加到HttpResponse对象(我们使用过滤器来跟踪发送到每个页面的视图状态的大小) 。在向浏览器发送PDF时,使用HTTP过滤器是没有意义的,因此清除过滤器可以解决我们的问题。我决定纯粹出于好奇心深入一点,并且如果其他人在将来遇到这个问题,就更新这个问题。

我在AppHarbor上有一个简单的应用程序,它重现了这个问题:http ://transferencodingtest.apphb.com/ 。如果同时选中“使用过滤器?” 和'发送大块?' 应该可以看到'transfer-encoding:chunked'标题(使用Chrome开发工具,Firebug,Fiddler等)。如果其中任何一个框未被选中,将得到一个合适的内容长度标题。底层代码在github上,所以你可以看到幕后发生了什么:

https://github.com/appakz/TransferEncodingTest

请注意,要在本地复制,您需要在IIS 7.5中设置本地网站(也可能有7个,我还没有尝试过)。Visual Studio附带的ASP .NET开发服务器不会重现这个问题。

我在这里添加了一些博客文章的更多细节:在ASP .NET中用'传输编码:块化'代替'Content-Length'标题

提问于
用户回答回答于

一旦Response.Flush()被调用,响应主体就被发送到客户端,所以没有额外的头文件可以被添加到响应中。我发现第二次调用可能性不大,那就是当时Response.Flush()添加Transfer-Encoding标题。

你说你启用了压缩。这几乎总是需要一个分块的响应。因此,如果服务器知道Content-Length压缩之前,它可能会有意义,它可能会将头替换为Transfer-Encoding头,并将响应块大小。但是,即使在服务器上启用了压缩,客户端也必须在其Accept-Encoding请求头中明确声明支持压缩,否则服务器不能压缩响应。你在测试中检查了吗?

最后一点,由于正在Response.Flush()手动调用,请尝试设置Response.Buffer = TrueResponse.BufferOutput = False。显然他们对如何Response.Flush()运作有冲突的影响。请参阅本页面底部的注释和本页面

用户回答回答于

MSDN上的文章看来,可以禁用分块编码:

appcmd set config /section:asp /enableChunkedEncoding:False

但是在ASP设置下提到它,所以它可能不适用于从ASP.NET处理程序生成的响应。

扫码关注云+社区