Сжатие ответа

Последнее обновление: 30.11.2022

Компрессия или сжатие ответа представляет один из инструментов, которые позволяют уменьшить объем отправляемых клиенту данных без потери самих данных.

Для компрессии ответа в рамках ASP.NET Core мы можем использовать либо внутренние средства конкретного веб-сервера: модуль динамического сжатия (Dynamic Compression module) в IIS, модуль mod_deflate в Apache или инструменты компрессии в Nginx. Однако эти инструменты не всегда бывают доступны, особенно когда приложение напрямую хостится в рамках таких веб-серверов как HTTP.sys или Kestrel. И в этом случае мы можем воспользоваться специальным компонентом middleware - Microsoft.AspNetCore.ResponseCompression. Рассмотрим, как использовать этот компонент.

Для этого возьмем проект ASP.NET Core и в файле Program.cs определим простейшее приложение, которое отправляет длинную строку и применяет кэширование:

var builder = WebApplication.CreateBuilder(args);

// добавляем сервисы сжатия
builder.Services.AddResponseCompression(options => options.EnableForHttps = true);

var app = builder.Build();

// подключаем сжатие
app.UseResponseCompression();

app.MapGet("/", () => "Lorem Ipsum is simply dummy text of the printing and typesetting industry...exact original form, accompanied by English versions from the 1914 translation by H. Rackham.");

app.Run();

Вначале нам надо подключить соответствующий сервис:

builder.Services.AddResponseCompression(options=>options.EnableForHttps = true);

Следует учитывать, что для протокола HTTPS по умолчанию отключено сжатие, поэтому в вызове данного метода подключается сжатие и для HTTPS. Однако если HTTPS не используется, то можно сократить вызов метода:

builder.Services.AddResponseCompression();

Затем в методе Configure задействуем middleware сжатия:

app.UseResponseCompression();

Запустим проект на выполнение:

Компрессия и сжатие ответа в приложении ASP.NET Core на C#

На уровне протокола HTTP это выражается также в установлении заголовка content-encoding, который сервер вместе с ответом посылает клиенту. По умолчанию этот заголовок имеет значение content-encoding: br, который говорит о том, что используется сжатие в формат Brotli

Теперь уберем из метода Configure применение компрессии - удалим или закомментируем следующую строку:

app.UseResponseCompression();

В этом случае мы получим немного другие значения.

Компрессия в веб-приложении ASP.NET Core на C# и метод UseResponseCompression

При использовании компрессии надо учитывать обратную сторону компрессии - уменьшение производительности: затраты на компрессию могут превосходить выгоду от уменьшения объема данных. Поэтому, для небольших объемов (меньше 1 кб) не очень рационально применение компрессии.

Установка заголовков типа ответа

При использования компрессии при отправке ответа нам обязательно надо установить MIME-тип отправляемых данных:

Так, в примере выше при отправке ответа в конечной точке

app.MapGet("/", () => "Lorem Ipsum ...

Заголовок типа ответа устанавливался автоматически. При использовании ASP.NET MVC или Razor Pages также необязательно указывать MIME-тип ответа. Однако не все middleware и компоненты в конвейере ASP.NET Core автоматически устанавливают заголовки. Например, при обработке запроса с помощью метода Run заголовки не устанавливаются, и их надо устанавливать вручную:

var builder = WebApplication.CreateBuilder(args);

// добавляем сервисы сжатия
builder.Services.AddResponseCompression(options => options.EnableForHttps = true);

var app = builder.Build();

// подключаем сжатие
app.UseResponseCompression();

app.Run(async (context) =>
{
    // устанавливаем MIME-тип ответа
    context.Response.ContentType = "text/plain"; // отправляем простой текст
    await context.Response.WriteAsync("Lorem Ipsum is simply dummy text of the printing and typesetting industry...exact original form, accompanied by English versions from the 1914 translation by H. Rackham.");
});

app.Run();

То есть в данном случае отправляем простой текст, поэтому MIME-тип text/plain. На данный момент middleware компрессии поддерживает следующие MIME-типы:

  • text/plain

  • text/css

  • application/javascript

  • text/html

  • application/xml

  • text/xml

  • application/json

  • text/json

Настройка сжатия

Метод AddResponseCompression() принимает делегат, который принимает объект ResponseCompressionOptions. Через свойства этого объекта можно установить параметры кэширования:

  • EnableForHttps устанавливает доступность сжатия для запросов по https

  • Providers представляет коллекцию провайдеров компрессии, которые применяются для сжатия ответа. Каждый провайлер представлен типом ICompressionProvider. Для добавления провайдера в эту коллекцию используется метод Add(), а для удаления - метод Remove().

  • MimeTypes определяет Mime-типы ответа, к которым применяется сжатие

  • ExcludedMimeTypes устанавливает Mime-типы ответа, к которым НЕ применяется сжатие

Например, исключим из сжатия простой текст:

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    // исключаем из сжатия простой текст
    options.ExcludedMimeTypes = new[] { "text/plain" };
});

Провайдеры компрессии

Механизм компрессии на уровне протокола HTTP сосредоточен вокруг двух заголовков. Вначале клиент в запросе посылает заголовок Accept-Encoding, в котором уведомляет сервер, какие форматы сжатия он поддерживает. Сервер, получив запрос, использует этот заголовок для выбора формата сжатия и затем посылает вместе с ответом клиенту заголовок Сontent-Encoding, в котором указывает использованный формат сжатия. Если клиент поддерживает сжатие в формат Brotli, то по умолчанию используется этот формат. Если клиент не поддерживает данный формат, то используется сжатия gzip.

Brotli Compression Provider

В примере выше по умолчанию использовалось сжатие в формат Brotli с помощью класса BrotliCompressionProvider. Он имеет свойство Level, которое устанавливает уровни сжатия и может принимать следующие значения:

  • CompressionLevel.Fastest: сжатие должно происходить как можно быстрее, даже если оно будет неоптимальным в плане уменьшения объема данных.

  • CompressionLevel.NoCompression: сжатие не применяется

  • CompressionLevel.Optimal: данные должны быть максимально сжаты, даже если для этого потребуется больше времени

Для изменения уровня компрессии можно применить метод Configure() объекта ServiceCollection:

using Microsoft.AspNetCore.ResponseCompression;
using System.IO.Compression;

var builder = WebApplication.CreateBuilder(args);

// добавляем сервисы сжатия
builder.Services.AddResponseCompression(options =>  options.EnableForHttps = true);
// устанавливаем уровень сжатия
builder.Services.Configure(options => options.Level = CompressionLevel.Optimal);

var app = builder.Build();

// подключаем сжатие
app.UseResponseCompression();

app.MapGet("/", () => "Lorem Ipsum is simply dummy text of the printing and typesetting industry...exact original form, accompanied by English versions from the 1914 translation by H. Rackham.");

app.Run();

GzipCompressionProvider

Также мы можем использовать сжатие gzip, а если точнее класс GzipCompressionProvider, который обеспечивает компрессию gzip. Он используется, если клиент не поддерживает сжатие в формат Brotli. Для его использования его надо добавить в коллекцию провайдеров сжатия:

using Microsoft.AspNetCore.ResponseCompression;
using System.IO.Compression;

var builder = WebApplication.CreateBuilder(args);

// добавляем сервисы сжатия
builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    // добавляем провайдер gzip-сжатия
    options.Providers.Add(new GzipCompressionProvider(new GzipCompressionProviderOptions()));
});

var app = builder.Build();

// подключаем сжатие
app.UseResponseCompression();

app.MapGet("/", () => "Lorem Ipsum is simply dummy text of the printing and typesetting industry...exact original form, accompanied by English versions from the 1914 translation by H. Rackham.");

app.Run();

В качестве параметра конструктор GzipCompressionProvider принимает объект GzipCompressionProviderOptions, который устанавливает настройки gzip-сжатия. Этот объект также имеет свойство Level, которое принимает те же значения, что и BrotliCompressionProvider:

builder.Services.Configure<GzipCompressionProviderOptions>(options => options.Level = CompressionLevel.Optimal);

В качестве альтернативы можно добавлять провайдеры типизацией метода Add():

options.Providers.Add<GzipCompressionProvider>();

Создание своих провайдеров

При необходимости мы можем создавать и применять свои провайдеры сжатия. Например, используем сжатие Deflate, и для этого изменим код программы следующим образом:

using Microsoft.AspNetCore.ResponseCompression;
using System.IO.Compression;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    // добавляем провайдер сжатия DeflateCompressionProvider
    options.Providers.Add(new DeflateCompressionProvider());
});
var app = builder.Build();

// подключаем сжатие
app.UseResponseCompression();

app.MapGet("/", () => "Lorem Ipsum is simply dummy text of the printing and typesetting industry...exact original form, accompanied by English versions from the 1914 translation by H. Rackham.");

app.Run();

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().

Стоит отметить, что если мы добавляем кастомный провайдер компрессии, то в случае если тип сжатия не поддерживается клиентом, то лучше также добавлять и провайдеры по умолчанию BrotliCompressionProvider и GzipCompressionProvider:

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    // добавляем провайдер сжатия DeflateCompressionProvider
    options.Providers.Add<DeflateCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
    options.Providers.Add<BrotliCompressionProvider>();
});

В итоге при получении запроса провайдеры будут перебираться в том порядке, в котором они добавлены. И первый попавшийся провайдер, формат сжатия которого поддерживается клиентом, и будет выбран для осуществления компрессии.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850