У меня проблемы с пониманием промежуточного ПО GZipStream ASP.Net Core

Я считаю, что неплохо разбираюсь в 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);
    }
}

Этот код извлечен из промежуточного программного обеспечения ASP.Net Core, которое сжимает ответ HTTP, и "на удивление", он работает... или так кажется (я тестировал его с помощью Fiddler).

Позвольте мне сначала выразить свое понимание:

  • Код начинается со ссылки на httpContext.Response.Body в responseStream.
  • Затем он заменяет ссылку httpContext.Response.Body вновь инициализированной memoryStream.
  • Если я понимаю, как работают ссылки C#, я говорю, что у нас все еще есть ссылка на исходные данные httpContext.Response.Body с responseStream, а httpContext.Response.Body новые данные пусты.
  • Затем мы вызываем следующее промежуточное ПО в конвейере.
  • Поскольку this.next() является ожидаемым, выполнение нашего кода «остановится» до тех пор, пока не вернутся все промежуточные программы.
  • Когда выполнение нашего кода «возобновляется», он инициализирует GZipStream, добавляет заголовок ответа и «ищет» начало memoryStream.
  • Наконец, он копирует содержимое или memoryStream в compressedStream, который записывает его в responseStream.

Итак, какова связь между memoryStream, compressedStream и responseStream? Мы создали compressedStream для записи в responseStream, а затем в httpContext.Response.Body, но ссылки с responseStream на httpContext.Response.Body больше нет?


person CodeAddict    schedule 26.02.2016    source источник


Ответы (2)


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);
    }
}

Надеюсь, это поможет.

person AndyCunningham    schedule 12.09.2018

Код просто берет то, что следующее промежуточное ПО в конвейере записало в объект memoryStream, и сжимает его для ответа клиенту тем, что было в responseStream до запуска этой части промежуточного ПО.

Итак, существующий responseStream контент + memoryStream контент.

person Simon Hardy    schedule 26.02.2016
comment
Если бы это было так просто; Я бы не спрашивал ;) httpContext.Response.Body это то, что возвращается клиенту; в какой части кода я ему что-либо пишу, кроме присвоения responseStream, не имеющего никакого содержания. - person CodeAddict; 28.02.2016
comment
Эти два утверждения: var responseStream = httpContext.Response.Body; и new GZipStream(responseStream, CompressionLevel.Optimal) - person Simon Hardy; 28.02.2016
comment
Это именно то, что меня смущает :| код инициализирует responseStream так, чтобы он указывал на httpContext.Response.Body; однако часть httpContext.Response.Body = memoryStream; удалила эту ссылку, и теперь httpContext.Response.Body это что-то другое... или есть что-то в ссылках .Net, которые мне здесь не хватает? - person CodeAddict; 10.03.2016
comment
Код, на который вы ссылаетесь, просто переназначает указатели, он не меняет значения. - person Simon Hardy; 11.03.2016
comment
Кажется, это глупый вопрос для вас, и вы можете подумать, почему вы не можете его получить? Вы пытаетесь ответить на вопрос, не указывая на часть (части), которую я неправильно понял и запутался. Да, responseStream содержит начальную версию httpContext.Response.Body, а compressedStream — ее сжатую версию. Теперь мы берем memoryStream, который пуст (он только что инициализирован), и записываем его содержимое (пусто) в compressedStream. Какая часть этого понимания неверна и почему? - person CodeAddict; 03.07.2016