我很难理解 GZipStream ASP.Net 核心中间件

本文关键字:Net 核心 中间件 ASP GZipStream 难理解 | 更新日期: 2023-09-27 18:36:19

我认为自己在 C# 方面相当不错,但我在理解以下代码时遇到了麻烦:

using (var memoryStream = new MemoryStream())
{
    var responseStream = httpContext.Response.Body;
    httpContext.Response.Body = memoryStream;
    await this.next(httpContext);
    using (var compressedStream = new GZipStream(responseStream, CompressionLevel.Optimal))
    {
        httpContext.Response.Headers.Add("Content-Encoding", new [] { "gzip" });
        memoryStream.Seek(0, SeekOrigin.Begin);
        await memoryStream.CopyToAsync(compressedStream);
    }
}

这段代码是从压缩HTTP响应的 ASP.Net 核心中间件中提取的,"令人惊讶的是",它可以工作......或者看起来是这样(我用小提琴手测试过)。

让我先说我的理解:

  • 代码首先引用responseStream中的httpContext.Response.Body
  • 然后httpContext.Response.Body引用替换为新初始化的memoryStream
  • 如果我了解 C# 引用的工作原理,我说我们仍然有对原始httpContext.Response.Body数据的引用,responseStream ,而httpContext.Response.Body新数据是空的。
  • 接下来,我们将调用管道中的下一个中间件。
  • 因为this.next()是等待的,所以我们的代码执行将"停止",直到所有中间件都返回。
  • 当我们的代码执行"恢复"时,它将初始化一个GZipStream,添加一个响应标头,并"寻找"到memoryStream的开头。
  • 最后,它将内容或memoryStream复制到compressedStream,将其写入responseStream

那么,memoryStreamcompressedStreamresponseStream之间的关系是什么呢?我们创建了compressedStream来写入responseStream,然后最终写入httpContext.Response.Body,但是从responseStreamhttpContext.Response.Body的引用不再存在了?

我很难理解 GZipStream ASP.Net 核心中间件

FWIW OOB ResponseCompressionMiddleware现在看起来有点不同。

但是在您粘贴的示例中,我将进行注释以说明为什么memoryStream在复制到compressedStream时实际上并不为空。

using (var memoryStream = new MemoryStream()) // Create a buffer so we can capture response content written by future middleware/controller actions
{
    var responseStream = httpContext.Response.Body; // save a reference to the ACTUAL STREAM THAT WRITES TO THE HTTP RESPONSE WHEN WRITTEN TO.
    httpContext.Response.Body = memoryStream; // replace HttpContext.Response.Body with our buffer (memoryStream).
    await this.next(httpContext); // <== somewhere in here is where HttpContext.Response.Body gets written to, and since Body now points to memoryStream, really memoryStream gets written to.
    using (var compressedStream = new GZipStream(responseStream, CompressionLevel.Optimal)) // Here is where we wrap the ACTUAL HTTP RESPONSE STREAM with our ready-to-write-to compressedStream.
    {
        httpContext.Response.Headers.Add("Content-Encoding", new [] { "gzip" });
        memoryStream.Seek(0, SeekOrigin.Begin); // Previously, we set HttpContext.Response.Body to a memoryStream buffer, then SOMEONE in the middleware chain or controller action wrote to that Body, we can seek to the beginning of what they wrote, and copy that content to the compressedStream.
        await memoryStream.CopyToAsync(compressedStream);
    }
}

希望有帮助。

代码只是获取管道中下一个中间件写入memoryStream对象的内容,并将其压缩,以便使用responseStream运行此中间件之前的任何内容响应客户端。

所以现有的responseStream内容+memoryStream内容。