Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Компрессия или сжатие ответа представляет один из инструментов, которые позволяют уменьшить объем отправляемых клиенту данных без потери самих данных.
Для компрессии ответа в рамках ASP.NET Core мы можем использовать либо внутренние средства конкретного веб-сервера: модуль динамического сжатия (Dynamic Compression module) в IIS, модуль mod_deflate в Apache или инструменты компрессии в Nginx. Однако эти инструменты не всегда бывают доступны, особенно когда приложение напрямую хостится в рамках таких веб-серверов как HTTP.sys или Kestrel. И в этом случае мы можем воспользоваться специальным компонентом middleware - Microsoft.AspNetCore.ResponseCompression. Рассмотрим, как использовать этот компонент.
Для этого создадим возбмем проект ASP.NET Core по типу Empty. Этот проект уже содержит нужный нам Nuget-пакет Microsoft.AspNetCore.ResponseCompression.
Теперь используем middleware в классе Startup:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace CompressionApp { public class Startup { public void ConfigureServices(IServiceCollection services) { // добавляем сервис компрессии services.AddResponseCompression(options=>options.EnableForHttps = true); } public void Configure(IApplicationBuilder app) { // подключаем компрессию app.UseResponseCompression(); app.Run(async context => { // отправляемый текст string loremIpsum = "Lorem Ipsum is simply dummy text ... including versions of Lorem Ipsum."; // установка mime-типа отправляемых данных context.Response.ContentType = "text/plain"; // отправка ответа await context.Response.WriteAsync(loremIpsum); }); } } }
Вначале нам надо подключить соответствующий сервис в методе ConfigureServices()
:
services.AddResponseCompression(options=>options.EnableForHttps = true);
Следует учитывать, что для протокола HTTPS по умолчанию отключено сжатие, поэтому в вызове данного метода подключается сжатие и для HTTPS. Однако если HTTPS не используется, то можно сократить вызов метода:
public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(); }
Затем в методе Configure задействуем middleware сжатия:
app.UseResponseCompression();
И еще важный момент для использования компрессии - при отправке ответа нам обязательно надо установить MIME-тип отправляемых данных:
context.Response.ContentType = "text/plain";
То есть в данном случае отправляем простой текст, поэтому MIME-тип text/plain
. На данный момент middleware компрессии поддерживает
следующие MIME-типы:
text/plain
text/css
application/javascript
text/html
application/xml
text/xml
application/json
text/json
При использовании ASP.NET MVC или Razor Pages необязательно указывать MIME-тип ответа.
Запустим проект на выполнение из под сервера Kestrel (не через IIS):
На уровне протокола HTTP это выражается также в установлении заголовка content-encoding, который сервер вместе с ответом посылает
клиенту. По умолчанию этот заголовок имеет значение content-encoding: br
, который говорит о том, что используется сжатие
в формат Brotli
Теперь уберем из метода Configure применение компрессии:
public void Configure(IApplicationBuilder app) { app.Run(async context => { string loremIpsum = "Lorem Ipsum is simply dummy text ... including versions of Lorem Ipsum."; await context.Response.WriteAsync(loremIpsum); }); }
В этом случае мы получим немного другие значения.
При использовании компрессии надо учитывать обратную сторону компрессии - уменьшение производительности: затраты на компрессию могут превосходить выгоду от уменьшения объема данных. Поэтому, для небольших объемов (меньше 1 кб) не очень рационально применение компрессии.
Механизм компрессии на уровне протокола HTTP концентрруется вокруг двух заголовков. Вначале клиент в запросе посылает заголовок Accept-Encoding, в котором уведомляет сервер, какие форматы сжатия он поддерживает. Сервер, получив запрос, использует этот заголовок для выбора формата сжатия и затем посылает вместе с ответом клиенту заголовок content-encoding, в котором указывает использованный формат сжатия. Если клиент поддерживает сжатие в формат Brotli, то по умолчанию используется этот формат. Если клиент не поддерживает данный формат, то используется сжатия gzip.
В примере выше по умолчанию использовалось сжатие в формат Brotli с помощью класса BrotliCompressionProvider. Он имеет свойство Level, которое устанавливает уровни сжатия и может принимать следующие значения:
CompressionLevel.Fastest: сжатие должно происходить как можно быстрее, даже если оно будет неоптимальным в плане уменьшения объема данных.
CompressionLevel.NoCompression: сжатие не применяется
CompressionLevel.Optimal: данные должны быть максимально сжаты, даже если для этого потребуется больше времени
Для изменения уровня компрессии, можно изменить метод ConfigureServices()
следующим образом:
using Microsoft.AspNetCore.ResponseCompression; using System.IO.Compression; //............................ public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(options=>options.EnableForHttps = true); services.Configure<BrotliCompressionProviderOptions>(options => { options.Level = CompressionLevel.Optimal; }); }
Также мы можем использовать сжатие gzip, а если точнее класс GzipCompressionProvider, который обеспечивает компрессию gzip. Он используется, если клиент не поддерживает сжатие в формат Brotli. Он также имеет свойство Level, которое принимает те же значения, что и BrotliCompressionProvider, и который мы можем настроить подобным образом:
public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(options=>options.EnableForHttps = true); services.Configure<GzipCompressionProviderOptions>(options => { options.Level = CompressionLevel.Optimal; }); }
При необходимости мы можем создавать и применять свои провайдеры сжатия. Например, используем сжатие Deflate, и для этого определим следующий класс:
using Microsoft.AspNetCore.ResponseCompression; using System.IO; using System.IO.Compression; namespace CompressionApp { public class DeflateCompressionProvider : ICompressionProvider { public string EncodingName => "deflate"; public bool SupportsFlush => true; public Stream CreateStream(Stream outputStream) { return new DeflateStream(outputStream, CompressionLevel.Optimal); } } }
Для создания провайдера реализуем интерфейс ICompressionProvider, в котором есть два свойства SupportsFlush и EncodingName и метод CreateStream.
Свойство EncodingName указывает на формат сжатия, который поддерживает клиент. Данный формат содержится в заголовке Accept-Encoding
в запросе к серверу,
наподобие:
Accept-Encoding: gzip, deflate, sdch, br
То есть данный провайдер будет срабатывать, если клиент прислал в запросе в заголовке Accept-Encoding значение deflate.
Свойство SupportsFlush указывает, поддерживается ли сброс записи в поток.
Метод CreateStream() возвращает сам поток ответа после сжатия или фактически обертка над изначальным потоком ответа, который передается в качестве параметра в метод. Для сжатия применяется встроенный класс DeflateStream().
Применим этот провайдер в методе ConfigureServices()
класса Startup:
public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(options => { options.EnableForHttps = true; options.Providers.Add(new DeflateCompressionProvider()); }); }
Для добавления провайдера вызывается метод options.Providers.Add()
.
Таким образом, если клиент допускает сжатие deflate, то будет использоваться именно провайдер DeflateCompressionProvider:
Стоит отметить, что если мы добавляем кастомный провайдер компрессии, то в случае если тип сжатия не поддерживается клиентом, то, согласно документации microsoft, следует добавлять и провайдеры по умолчанию BrotliCompressionProvider и GzipCompressionProvider:
public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(options => { options.EnableForHttps = true; options.Providers.Add(new DeflateCompressionProvider()); options.Providers.Add<BrotliCompressionProvider>(); options.Providers.Add<GzipCompressionProvider>(); }); }
В итоге при получении запроса провайдеры будут перебираться в том порядке, в котором они добавлены. И первый попавшийся провайдер, формат сжатия которого поддерживается клиентом, и будет выбран для осуществления компрессии.
Выше был перечислен весь набор MIME-типов по умолчанию, которые используются в Microsoft.AspNetCore.ResponseCompression, но при необходимости мы можем добавить новый MIME-тип:
services.AddResponseCompression(options => { options.EnableForHttps = true; options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml", "application/atom+xml" }); options.Providers.Add<GzipCompressionProvider>(); });
Если мы используем в качестве прокси-сервера IIS, то мы можем воспользоваться модулем динамической компрессии, более того по умолчанию он включен. Если
же модуль необходимо отключить, то в проекте в файле конфигурации web.config нужно добавить элемент
<urlCompression> и указать у него атрибут doStaticCompression="false"
Если мы работаем с Visual Studio 2017/2019, то в ней по умолчанию файл web.config отсутствует. Поэтому добавим его:
Изменим файл web.config, использовав элемент urlCompression:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/> </handlers> <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/> <urlCompression doDynamicCompression="true"/> </system.webServer> </configuration>
Но даже если мы используем IIS, это не значит, что мы не можем вместе с этим использовать и компонент Microsoft.AspNetCore.ResponseCompression
, правда, это не сильно увеличит сжатие.
К примеру, были протестированы различные ситуации в использовании компрессии для одного и того же динамически формируемого документа html:
IIS без модуля динамической компрессии - 7,7 кБ
IIS с включенным модулем динамической компрессии - 2,5 кБ
IIS с включенным модулем динамической компрессии и Microsoft.AspNetCore.ResponseCompression - 2,3 кБ
Kestrel без Microsoft.AspNetCore.ResponseCompression - 7,5 кБ
Kestrel с Microsoft.AspNetCore.ResponseCompression - 2,1 кБ
Таким образом, сжатие, что через Microsoft.AspNetCore.ResponseCompression, что через модуль динамической компрессии IIS, дает неплохое уменьшение объема отправляемых данных, в то же время различные ситуации сжатия несильно будут отличаться.