Start Debugging

ASP.NET Core 11 における Minimal APIs vs コントローラー:2026 年はどちらを選ぶべきか?

ASP.NET Core 11 ではデフォルトで Minimal APIs を選びます。コントローラーは、Minimal APIs がまだカバーしていない MVC の機能、つまり多数のアクションに対する規約ベースのルーティング、MVC スタイルのフィルター、Razor ビューが必要な場合にのみ使います。

ASP.NET Core 11 で新しい HTTP サービスを始めるにあたって Minimal APIs とコントローラーのどちらにするか悩んでいるなら、Minimal APIs を選んでください。2022 年当時にコントローラーを使い続ける理由になっていたもの(フィルターがない、バリデーションがない、OpenAPI が弱い、ルートグループがない、Native AOT がない)はもうありません。.NET 11 から、Minimal APIs にはエンドポイントフィルター、ルートグループ、Microsoft.AspNetCore.OpenApi による組み込みの OpenAPI ドキュメント、Microsoft.AspNetCore.Http.Validation[Validate] によるパラメーターバリデーション、TypedResults、そして完全な Native AOT サポートがあります。コントローラーは次の 2 つの特定のケースで依然として正しい選択です。1 つは同じプロジェクトで Razor ビュー(MVC または Razor Pages)が必要な場合、もう 1 つは現在動作している [Route] 付きアクションを数百個抱える大規模な既存コードベースを保守している場合です。

この記事のすべてのコードサンプルは <TargetFramework>net11.0</TargetFramework><LangVersion>14.0</LangVersion> を対象とし、.NET 11 GA SDK で同梱される ASP.NET Core 11 のパッケージを使います。

機能マトリックス

機能Minimal APIs (ASP.NET Core 11)Controllers (ASP.NET Core 11)
ルーティングエンドポイントルーティング、MapGet/MapPost、ルートグループエンドポイントルーティング、属性または規約ベース
エンドポイントごとのフィルターIEndpointFilterAddEndpointFilter<T>IActionFilterIAsyncActionFilter
モデルバインディング元の推論パラメーターバインディング規則、[FromBody] は任意[FromBody][FromQuery][FromForm] など
バリデーションASP.NET Core 11 の Microsoft.AspNetCore.Http.Validation における [Validate]ModelState と DataAnnotations、ApiController
結果ヘルパーTypedResultsResultsIActionResultActionResult<T>Problem()
OpenAPI / SwaggerMicrosoft.AspNetCore.OpenApi 11、追加配線なしMicrosoft.AspNetCore.OpenApi 11 + 規約
Native AOT完全サポート(PublishAot=true がそのまま動く)限定的。AddControllers は依然 trim 警告を出す
Razor ビュー / Razor Pages非対応(ビューレンダリングパイプラインがない)対応(AddControllersWithViews、Razor Pages)
Antiforgery(cookie を伴うフォーム POSTapp.UseAntiforgery() + [FromForm][ValidateAntiForgeryToken] で MVC にデフォルト組み込み
エンドポイントあたりの既定ボイラープレートラムダ 1 つ + MapX 1 行クラス 1 つ + メソッド 1 つ + 属性
200 エンドポイント規模での発見しやすさグループ単位のファイル / 拡張メソッドリソースごとに 1 クラスのコントローラー
AOT 公開時のバイナリサイズフレームワーク最小より大きい。フル MVC パイプライン
スループット(小さな JSON エンドポイント、TechEmpower スタイル)コントローラーより約 3-5% 高いベースライン

最終行の数値は .NET チーム自身の ASP.NET Core 11 ベンチマークから来ています。ほとんどのアプリでは、その差は「ノイズに近い」と考えてください。Minimal APIs を優先する理由はスループットの差ではありません。表面積が小さいこと、そして AOT のストーリーです。

Minimal APIs が正しい選択になるとき

ASP.NET Core 11 の新しい HTTP サービスでは、デフォルトで Minimal APIs を使います。輝く具体的なケース:

小さいけれど現実的なエンドポイント:

// .NET 11, C# 14
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddScoped<IInvoiceStore, SqlInvoiceStore>();

var app = builder.Build();
app.MapOpenApi();

var invoices = app.MapGroup("/invoices")
    .WithTags("Invoices")
    .RequireAuthorization();

invoices.MapGet("/{id:int}", async (
    int id,
    IInvoiceStore store,
    CancellationToken ct) =>
{
    var invoice = await store.FindAsync(id, ct);
    return invoice is null
        ? Results.NotFound()
        : TypedResults.Ok(invoice);
});

invoices.MapPost("/", async Task<Results<Created<Invoice>, ValidationProblem>> (
    [Validate] CreateInvoice request,
    IInvoiceStore store,
    CancellationToken ct) =>
{
    var created = await store.CreateAsync(request, ct);
    return TypedResults.Created($"/invoices/{created.Id}", created);
});

app.Run();

CreateSlimBuilder は最小限のサービスセットだけを読み込み、これが Native AOT を実用化しています。MapGroupRequireAuthorization の組み合わせは、.NET 7 より前には存在しなかったルートグループ / 共有ポリシーの仕組みです。[Validate] は ASP.NET Core 11 の属性で、ソースジェネレーターに支えられたバリデーター Microsoft.AspNetCore.Http.Validation を有効化し、これまでコントローラー専用だった DataAnnotations パイプラインを置き換えます。戻り値型 Results<TOk, TErr> は、追加の Produces 装飾なしで OpenAPI ドキュメントを正確に保つための仕組みです。

コントローラーが正しい選択になるとき

次のいずれかが当てはまる場合はコントローラーを使います。

同等の請求書エンドポイントのコントローラー:

// .NET 11, C# 14
[ApiController]
[Route("invoices")]
[Authorize]
public class InvoicesController(IInvoiceStore store) : ControllerBase
{
    [HttpGet("{id:int}")]
    [ProducesResponseType(typeof(Invoice), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<Invoice>> Get(int id, CancellationToken ct)
    {
        var invoice = await store.FindAsync(id, ct);
        return invoice is null ? NotFound() : Ok(invoice);
    }

    [HttpPost]
    [ProducesResponseType(typeof(Invoice), StatusCodes.Status201Created)]
    [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<Invoice>> Create(
        CreateInvoice request,
        CancellationToken ct)
    {
        var created = await store.CreateAsync(request, ct);
        return CreatedAtAction(nameof(Get), new { id = created.Id }, created);
    }
}

エンドポイントが 2 つ、ボイラープレートは 2 倍、機能的な得はありません。これが 2026 年に Minimal APIs を既定にする理由です。新しいコードでは、同じことを表現するためにより少ない量を書きます。

ベンチマーク

ASP.NET Core 11 における 2 つのスタイルの性能差は、小さな JSON エンドポイントでは小さいながらも一貫しています。.NET チームの公式ベンチマーク(https://github.com/aspnet/Benchmarks 、ASP.NET Core 11 GA の結果は 2025 年 11 月公開)は次のように報告しています。

シナリオMinimal APIs (RPS)Controllers (RPS)
GET /json(単一オブジェクト、200 バイト)1,160,0001,120,000+3.6%
POST /json(エコー、ボディ 1 KB)590,000565,000+4.4%
GET /plaintext(TechEmpower)7,250,0007,250,0000%
コールドスタート(AOT、dotnet publish -aot70 ms280 ms-75%

公開された実行から要約した方法論:ASP.NET Core 11 GA、Linux x64、Citrine マシン、wrk で Kestrel を 256 同時接続で叩く、30 秒間の実行を 5 サンプル平均。コールドスタートの行は同じラボから、初回実行時の AOT バイナリに time を当てた値です。plaintext が同一なのは、リクエストパイプラインのコストがエンドポイント形状ではなく Kestrel のパースに支配されているからです。

数字を素直に読むとこうです。ホットな JSON エンドポイントで CPU バウンドなら、コントローラーから Minimal APIs に乗り換えることで 3-5% 回収できるかもしれません。実際のアプリは時間を EF Core や HTTP 送信側に費やしていて、エンドポイントディスパッチャーではありません。一方、コールドスタートの行は本物です。AWS Lambda や Azure Functions に展開しているなら、AOT の差は「1 秒未満で温まるサービス」と「そうでないサービス」の違いそのものです。.NET 11 Lambda のコールドスタートを減らす という関連記事で、これらの数値を生み出している AOT 公開のフローを掘り下げています。

あなたに代わって決めてしまう落とし穴

3 つの制約は、好みに関わらず結論を決めます。

Minimal APIs がまだやらないこと

コントローラーにできて、ASP.NET Core 11 の Minimal APIs にまだ追いついていないことがいくつかあります。

設計の中でこれら 3 つのいずれかが要となっているなら、損益分岐はコントローラー側に再び傾きます。新しい ASP.NET Core 11 サービスの 95% では、そうではありません。

選択、もう一度

ASP.NET Core 11 ではデフォルトで Minimal APIs を選びます。.NET 11 で JSON HTTP サービスを作るうえで、ボイラープレートが少なく、AOT に対応し、OpenAPI もきれいに出る方法です。コントローラーを好む歴史的な理由(フィルター、バリデーション、ルートグループ、OpenAPI、規約)は、.NET 7、8、10、11 を通じて 1 つずつ閉じられました。残っているのは、コントローラーが依然として持つ小さな MVC 機能集合(Razor ビュー、モデルバインダー、アクション制約)です。プロジェクトでそれが必要なら、必要な部分にはコントローラーを、残りには Minimal APIs を使います。2 つのスタイルは 1 つの WebApplication で問題なく共存します。

関連

ソース

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< 戻る