我无法理解GZipStream ASP.Net核心中间件

时间:2016-02-26 16:54:18

标签: c# asp.net-core gzipstream

我认为自己在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 Core中间件中提取的,并且令人惊讶地"它可以工作......或者看起来似乎(我用Fiddler测试过它)。

首先让我理解:

  • 代码首先引用httpContext.Response.Body中的responseStream
  • 然后用新初始化的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的引用不再存在?

2 个答案:

答案 0 :(得分:1)

现在,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);
    }
}

希望有帮助。

答案 1 :(得分:0)

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

现有的responseStream内容+ memoryStream内容。