1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

.NET 8ベースでZStandardのリクエスト/レスポンス圧縮に対応する

1
Last updated at Posted at 2026-03-03

はじめに

.NET 11 Preview 1で ZStandard (zstd) が標準サポートされました。
HTTP 圧縮といえば gzip や Brotli が一般的ですが、圧縮率や圧縮速度の高さからリバースプロキシなどでも ZStandard のサポートが進んでいるため、時代に合ったアップデートだと思います。

この対応はこのNoteの記事で説明されているように簡単に導入できますが、.NET 11を待てない環境も多いと思います。この記事では.NET 8でリクエスト/レスポンス圧縮にZStandardを利用する方法について解説します。

ZstdNetを使って対応してみる

ちょっとした確認の場合、.NET SDK 10 以降を導入するとプログラミングの敷居が下がるので、.NET 8.0 しか使わない人も .NET SDK は 10 以降を導入するのがおすすめです。

まずはProgram.csというファイルを作り、VSCodeでそのファイルを開きます。

mkdir zstd-sample
cd zstd-sample
code Program.cs

Program.csでは次の処理を行っています。

  1. ZStandardの解凍・圧縮ライブラリであるZstdNetでプロバイダーを作成
  2. AddRequestDecompressionAddResponseCompressionでサービスを登録
  3. UseRequestDecompressionUseResponseCompressionでパイプラインに組み込む

詳しくは、ASP.NETの応答の圧縮を確認してください。

また、動作確認用に単純な2つのエンドポイントを実装しています。どちらもZstandardの解凍・圧縮処理は明示的には呼び出していません。

  • 指定されたサイズのランダムな文字列を返却する (GET /CompressionSample)
  • 受け取ったデータをそのままエコーする (POST /DecompressionSample)
Program.cs
#:sdk Microsoft.NET.Sdk.Web
#:property TargetFramework=net8.0
#:package ZstdNet@1.5.7

using Microsoft.AspNetCore.Mvc;
using System.Text.Json.Serialization;

var builder = WebApplication.CreateBuilder(args);
builder.Services.ConfigueHttpJsonOptions(o =>
{
    o.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});

// 2. `AddRequestDecompression`と`AddRequestDecompression`でサービスを登録
builder.Services.AddRequestDecompression(options =>
{
    options.DecompressionProviders.Add("zstd", new ZstdCompressionProvider());
});
builder.Services.AddResponseCompression(options =>
{
    options.Providers.Add<ZstdCompressionProvider>();
});

var app = builder.Build();
// 3. `UseRequestDecompression`と`UseResponseCompression`でパイプラインに組み込む
app.UseRequestDecompression();
app.UseResponseCompression();

var random = new Random();
// ランダムな文字列を返却する
app.MapGet("/CompressionSample", (int size) 
    => new CompressionMessage(new string(Enumerable.Range(0, size)
        .Select(_ => (char)('a' + random.Next(26)))
        .ToArray())));
// 受け取ったデータをそのままエコーする
app.MapPost("/DecompressionSample", ([FromBody] CompressionMessage message) => message.Data);
app.Run();

// Dto
public record CompressionMessage(string Data);
[JsonSerializable(typeof(CompressionMessage))]
public partial class AppJsonContext : JsonSerializerContext { }

// 1.ZStandardの解凍・圧縮ライブラリであるZstdNetでプロバイダーを作成
public class ZstdCompressionProvider 
    : Microsoft.AspNetCore.RequestDecompression.IDecompressionProvider,
      Microsoft.AspNetCore.ResponseCompression.ICompressionProvider
{
    public string EncodingName => "zstd";
    public bool SupportsFlush => true;

    public Stream CreateStream(Stream outputStream)
        => new ZstdNet.CompressionStream(outputStream);

    public Stream GetDecompressionStream(Stream stream)
        => new ZstdNet.DecompressionStream(stream);
}

実行して結果を確認します。

 dotnet run Program.cs

ランダムな文字を生成しているため圧縮結果はそこまでよくありませんが、Accept-Encodingに従い圧縮されたメッセージがダウンロードされたことを確認できます。

受信
 curl.exe -s `
    "http://localhost:5000/CompressionSample?size=10000" `
    -H "Accept-Encoding: zstd" `
    -o response.zst `
    -w "Response (compressed): %{size_download} bytes\nHTTP status:%{http_code}\nTotal time :%{time_total}s"
Response (compressed): 6043 bytes
HTTP status:200
Total time :0.004160s
 zstd -d response.zst
 ls

    Directory: C:\temp\zstd-sample

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2026/03/02    11:37           1852 Program.cs
-a---          2026/03/02    11:38          10011 response
-a---          2026/03/02    11:38           6043 response.zst

同様に、ダウンロードしたzstで圧縮されたファイルをそのままアップロードしてみます。
Content-Encodingにzstdを指定して、コンテンツがZStandardで圧縮されていること宣言しています。

送信
 curl.exe -X POST "http://localhost:5000/DecompressionSample" `
   -H "Content-Type: application/json" `
   -H "Content-Encoding: zstd" `
   --data-binary "@response.zst" `
   -w "Response (compressed): %{size_download} bytes\nHTTP status:%{http_code}\nTotal time :%{time_total}s"
ouysharwzktnsvrcnqpxiiqyfzdxddqkhp ......
HTTP status:200
Total time :0.005758s

Windows に zstd コマンドが無い場合は winget でインストールできます。

❯ winget install Meta.Zstandard

Dockerコンテナとしてビルドする

ZstdNetはWindowsやLinuxなどのネイティブモジュールをラップしたライブラリですが、パブリッシュ時にランタイム識別子を識別し必要なモジュールをパッケージに含めるように設定されています。

dotnet publishコマンドを使って直接Dockerコンテナをビルドする機能が追加されたのでそのままコンテナイメージを作成できます。
ただし、Windows上で Linux コンテナを Native AOT 付きでビルドしようとすると次のエラーが発生します。

error Cross-OS native compilation is not supported.

WindowsでLinuxコンテナをビルドする場合はAOTを無効にする必要があります。

❯ dotnet publish Program.cs `
    -c Release `
    -t:PublishContainer `
    --self-contained false `
    -p:PublishAot=false `
    -p:ContainerImageName=zstd-sample

WSL上でビルドすればAOTありでコンテナを作成できます。
もしくは、自前でDockerfileを記述することでもこの問題に対応できます。

$ dotnet publish Program.cs ¥
    -c Release ¥
    -t:PublishContainer ¥
    --self-contained false ¥
    -p:ContainerImageName=zstd-sample

ビルドされたコンテナを同じように実行して結果を確認することができます。

❯ docker run -d -p 8080:8080 zstd-sample
❯ curl.exe -s `
     "http://localhost:8080/CompressionSample?size=10000" ¥
     -H "Accept-Encoding: zstd" ¥
     -o response.zst ¥
     -w "Response (compressed): %{size_download} bytes\nHTTP status:%{http_code}\nTotal time :%{time_total}s"
Response (compressed): 6040 bytes
HTTP status:200
Total time :0.175902s

おわりに

今回の記事で.NET 8でもZStandardの対応はそんなに難しくないことが確認できたと思います。
今後速度とのバランスや将来性を考えると ZStandard は今後より一般的になる可能性があります。
正式リリース後の挙動も改めて確認してみたいと思います。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?