Já abordei em outro artigo o suporte à compressão de respostas no ASP.NET Core (presente desde o release 1.1), demonstrando o uso deste tipo de técnica baseado no formato GZip em uma API REST:
Desenvolvido inicialmente pela Google e representando uma evolução do padrão GZip, o Brotli é atualmente suportado por browsers como Microsoft Edge, Chrome e Mozilla Firefox.
Neste novo artigo pretendo retomar esta questão envolvendo mecanismos de compressão em aplicações Web, apresentando desta vez uma novidade que integra a versão 2.1 do ASP.NET Core: o suporte ao algoritmo conhecido como Brotli.
Detalhes da aplicação de testes
A aplicação utilizada nos testes descritos neste artigo já está disponível no GitHub. Implementada em ASP.NET Core 2.1, a API REST em questão conta com uma versão que faz uso de GZip e outra de Brotli:
Além de abordar o uso de compressão, este mesmo exemplo demonstra ainda a remoção de valores nulos na resposta produzida por uma API (outro ajuste que pode contribuir significativamente para a geração de um menor volume de dados).
Na listagem a seguir temos a implementação da classe Produto:
namespace APIProdutos.Models
{
public class Produto
{
public string CodProduto;
public string NomeProduto;
public double Preco;
public double? Teste1;
public double? Teste2;
public double? Teste3;
public double? Teste4;
}
}
Já a próxima listagem traz o código que define o tipo ProdutosController, o qual produzirá a resposta a ser comprimida:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using APIProdutos.Models;
namespace APIProdutos.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProdutosController : ControllerBase
{
[HttpGet]
public IEnumerable<Produto> Get()
{
List<Produto> produtos = new List<Produto>();
Produto prod;
for (int i = 1; i <= 100; i++)
{
prod = new Produto();
prod.CodProduto = i.ToString("0000");
prod.NomeProduto = string.Format("PRODUTO {0:0000}", i);
prod.Preco = i / 10.0;
produtos.Add(prod);
}
return produtos;
}
}
}
Na seguinte imagem é possível observar três cenários envolvendo essa API (os testes empregaram o utilitário Fiddler no monitoramento das respostas geradas):
- Uma consulta sem remoção de valores nulos e sem compressão de resposta (11.915 bytes);
- Uma consulta com remoção de valores nulos e sem compressão de resposta (6.315 bytes);
- Uma consulta com remoção de valores nulos e com compressão de resposta (768 bytes).
Habilitando o uso de Brotli
O primeiro passo para tornar possível a utilização do algoritmo Brotli na compressão de respostas de uma aplicação ASP.NET Core será a codificação de um provider baseado neste formato. Na listagem a seguir está o código que define o tipo BrotliCompressionProvider, com o mesma implementando a interface ICompressionProvider (namespace Microsoft.AspNetCore.ResponseCompression):
using System.IO;
using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;
namespace APIProdutos
{
public class BrotliCompressionProvider : ICompressionProvider
{
public string EncodingName => "br";
public bool SupportsFlush => true;
public Stream CreateStream(Stream outputStream) => new BrotliStream(outputStream, CompressionLevel.Optimal, true);
}
}
Ajustes também serão realizados na classe Startup:
Em ConfigureServices foi especificado o uso da classe BrotliCompressionProvider, através de uma chamada ao método AddResponseCompression (foi definido ainda que este comportamento ocorrerá em conjunto com a utilização de HTTPS).
Ao acionar o método UseResponseCompression em Configure o middleware de compressão de respostas será ativado, empregando para isto as configurações realizadas em ConfigureServices.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;
namespace APIProdutos
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Configura o modo de compressão
services.AddResponseCompression(options =>
{
options.Providers.Add<BrotliCompressionProvider>();
options.EnableForHttps = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_0)
.AddJsonOptions(opcoes => // Remove valores nulos das respostas
{
opcoes.SerializerSettings.NullValueHandling =
Newtonsoft.Json.NullValueHandling.Ignore;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
// Ativa a compressão
app.UseResponseCompression();
app.UseMvc();
}
}
}
Como resultado teremos uma redução para 423 bytes, conforme indicado na próxima imagem: