ASP.NET Core 6 Web API如何获取Gzip压缩响应的大小?
获取ASP.NET Core 6中Gzip压缩后的响应体大小
直接读取Response.ContentLength会出错,原因是Gzip压缩是动态流式处理的,压缩后的响应流不支持直接获取预定义长度,且流会被压缩中间件封装为无法直接读取长度的包装流。以下是可行的实现方案:
1. 实现字节数跟踪流
创建一个包装流,用于统计写入到响应流的总字节数:
public class SizeTrackingStream : Stream { private readonly Stream _innerStream; public long TotalBytesWritten { get; private set; } public SizeTrackingStream(Stream innerStream) { _innerStream = innerStream; } public override bool CanRead => _innerStream.CanRead; public override bool CanSeek => _innerStream.CanSeek; public override bool CanWrite => _innerStream.CanWrite; public override long Length => _innerStream.Length; public override long Position { get => _innerStream.Position; set => _innerStream.Position = value; } public override void Flush() => _innerStream.Flush(); public override int Read(byte[] buffer, int offset, int count) => _innerStream.Read(buffer, offset, count); public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin); public override void SetLength(long value) => _innerStream.SetLength(value); public override void Write(byte[] buffer, int offset, int count) { TotalBytesWritten += count; _innerStream.Write(buffer, offset, count); } public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { TotalBytesWritten += count; await _innerStream.WriteAsync(buffer, offset, count, cancellationToken); } }
2. 编写响应大小跟踪中间件
通过中间件替换响应流为跟踪流,在压缩完成后获取字节数:
public class ResponseSizeMiddleware { private readonly RequestDelegate _next; public ResponseSizeMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { var originalStream = context.Response.Body; try { using var trackingStream = new SizeTrackingStream(originalStream); context.Response.Body = trackingStream; // 执行后续管道,让压缩中间件处理响应 await _next(context); // 获取压缩后的响应大小 var compressedSize = trackingStream.TotalBytesWritten; // 可选:将大小写入自定义响应头,返回给客户端 context.Response.Headers.Add("X-Compressed-Size", compressedSize.ToString()); // 可选:记录到日志(需注入ILogger) // _logger.LogInformation($"Compressed response size: {compressedSize} bytes"); // 可选:存入HttpContext.Items,供控制器读取 // context.Items["CompressedResponseSize"] = compressedSize; } finally { context.Response.Body = originalStream; } } }
3. 正确注册中间件
在Program.cs中,必须将跟踪中间件注册在压缩中间件之后,确保捕获的是压缩后的响应流:
var builder = WebApplication.CreateBuilder(args); // 添加压缩服务 builder.Services.AddControllers(); builder.Services.AddResponseCompression(options => { options.Providers.Add<GzipCompressionProvider>(); options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json" }); }); var app = builder.Build(); // 注册顺序:先压缩中间件,再跟踪中间件 app.UseResponseCompression(); app.UseMiddleware<ResponseSizeMiddleware>(); app.MapControllers(); app.Run();
注意事项
- 中间件顺序不可颠倒,否则捕获的是压缩前的响应大小。
- 不要依赖
Response.ContentLength属性,该值在动态压缩场景下会是null或不准确。 - 如果需要在控制器中获取压缩大小,可通过
HttpContext.Items["CompressedResponseSize"]读取。
内容的提问来源于stack exchange,提问作者ABHIJEET SINGH
相关产品推荐
相关产品推荐

