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

时间:2015-11-30 12:12:56

标签: asp.net .net http asp.net-core asp.net-core-mvc

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

令我惊讶的是,在ASP.NET 5中完成这是一项非常棘手的任务。一切都在Kestrel上运行,它来自ASP.NET 5 Beta7 supports writing chunked responses automatically when no content length is specified for the response

有一个类似的问题here,但区别在于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);
        }
     }
}

然而,这是非常低效的,因为我们在处理每个请求时使用额外的内存。使用仅包裹context.Response.Body流并且另外计算字节的自定义流不起作用,因为Microsoft团队声明once the amount of data they're willing to buffer has been written, it goes out on the wire and they don't store it any more,所以我最多可以将Content-Length添加到最后一个块这不是希望的行为 - 我想完全避免分块。

非常感谢任何帮助!

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

1 个答案:

答案 0 :(得分:3)

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

正如你所说context.Response.Body流在发送之前没有存储太多,所以开销不是那么大。

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

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

关于你的第二个问题:这个地方是middlware。