在 ASP.NET 5 响应中设置 Content-Length 标头

Mil*_*kic 6 .net asp.net http asp.net-core-mvc asp.net-core

TL; DR我有一个 ASP.NET 5 (MVC 6) 应用程序,只是尝试设置 HTTP Content-Length 标头以避免分块响应。

令我惊讶的是,这在 ASP.NET 5 中是一项相当棘手的任务。一切都在 Kestrel 上运行,从 ASP.NET 5 Beta7 开始,当没有为响应指定内容长度时,它支持自动写入分块响应

这里有一个类似的问题,但不同之处在于OP只想计算响应大小,而我需要确保Content-Length标头在响应中发送。到目前为止尝试了很多事情,唯一有效的是编写一个自定义中间件:

public class WebApiMiddleware {
    RequestDelegate _next;

    public WebApiMiddleware(RequestDelegate next) {
        _next = next;
    }

    public async Task Invoke(HttpContext context) {
         using (var buffer = new MemoryStream()) {
            var response = context.Response;

            var bodyStream = response.Body;
            response.Body = buffer;

            await _next(context);

            response.Headers.Add("Content-Length", new[] { buffer.Length.ToString()});
            buffer.Position = 0;
            await buffer.CopyToAsync(bodyStream);
        }
     }
}
Run Code Online (Sandbox Code Playgroud)

然而,这是非常低效的,因为我们在处理每个请求时都使用额外的内存。使用仅环绕流context.Response.Body并另外计算字节的自定义流是行不通的,因为 Microsoft 团队声明,一旦写入了他们愿意缓冲的数据量,数据就会通过网络传输,而他们不会不再存储它,所以我最多可以将 Content-Length 添加到最后一个块,这不是期望的行为 - 我想完全避免分块。

非常感谢任何帮助!

ps 我知道分块是 HTTP/1.1 的常规功能,但在我的场景中它会降低性能,因此我想避免它而不强制客户端发送 HTTP/1.0 请求。

Pav*_*ets 3

您需要在发送响应之前发送标头,因此您需要在发送之前存储整个响应以确定长度。

正如您所说,context.Response.Body流在发送之前不会存储太多,因此开销并没有那么大。

有一个中间件可以做类似的事情: https: //github.com/aspnet/BasicMiddleware/blob/master/src/Microsoft.AspNetCore.Buffering/ResponseBufferingMiddleware.cs它与您编写的非常相似,但支持其他功能,例如IHttpBufferingFeature允许其他中间件控制缓冲。

检查bufferStream.CanSeek是为了确保没有人已经将响应流包装到缓冲流中并避免双缓冲。

对于你的第二个问题:这个地方是中间件。