Имам проблеми с разбирането на GZipStream ASP.Net Core Middleware

Смятам, че съм доста добър в 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