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请求。
答案 0 :(得分:3)
您需要在发送响应之前发送标头,因此您需要在发送之前存储整个响应以确定长度。
正如你所说context.Response.Body
流在发送之前没有存储太多,所以开销不是那么大。
有中间件做类似的事情:https://github.com/aspnet/BasicMiddleware/blob/dev/src/Microsoft.AspNet.Buffering/ResponseBufferingMiddleware.cs它与您编写的内容非常相似,但支持其他功能,如IHttpBufferingFeature
,以允许其他中间件控制缓冲。
检查bufferStream.CanSeek
是否确保没有人将响应流包装到缓冲流中并避免双重缓冲。
关于你的第二个问题:这个地方是middlware。