dotnet-api-versioning について
Asp.Versioning を使って Minimal APIs と MVC の API バージョンを管理する、ASP.NET Core 開発者向けガイドです。複数の読み取り方式、廃止通知、旧パッケージからの移行を扱います。
主な機能
- 01URL・ヘッダー・クエリでのバージョン管理
- 02Minimal APIs と MVC の設定
- 03複数のバージョン読み取り方式の併用
- 04Sunset ポリシーの設定
- 05旧 NuGet パッケージからの移行
活用シーン
- 01新しい API のバージョン方式の選定
- 02廃止バージョンと移行の利用者への通知
- 03旧バージョン管理パッケージから Asp.Versioning への移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-api-versioningインストール後、Claude Code で/dotnet-api-versioningを入力して読み込まれたか確認します。
詳細
dotnet-api-versioning
Asp.Versioning ライブラリファミリーを使用した ASP.NET Core の API バージョニング戦略です。シンプルで見つけやすいため、URL セグメントによるバージョニング(/api/v1/)を推奨します。このスキルでは、Minimal API と MVC コントローラーの両方で使える URL、ヘッダー、クエリ文字列によるバージョニング、サンセットポリシーの適用、レガシーパッケージからの移行について説明します。
対象外: Minimal API のエンドポイントパターン(ルートグループ、フィルター、TypedResults)については、[skill:dotnet-minimal-apis] を参照してください。API バージョンごとの OpenAPI ドキュメント生成については、[skill:dotnet-openapi] を参照してください。バージョンごとの認証と認可については、[skill:dotnet-api-security] を参照してください。
相互参照:[skill:dotnet-minimal-apis] は Minimal API のエンドポイントパターンについて、[skill:dotnet-openapi] はバージョン管理された OpenAPI ドキュメントについて説明しています。
パッケージの概要
| パッケージ | 対象 | ステータス |
|---|---|---|
Asp.Versioning.Http | Minimal API | 現行 |
Asp.Versioning.Mvc.ApiExplorer | MVC コントローラー + API Explorer | 現行 |
Asp.Versioning.Mvc | MVC コントローラー(API Explorer なし) | 現行 |
Microsoft.AspNetCore.Mvc.Versioning | MVC コントローラー | レガシー -- Asp.Versioning.Mvc に移行してください |
Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer | MVC + API Explorer | レガシー -- Asp.Versioning.Mvc.ApiExplorer に移行してください |
Minimal API 用のインストール:
<PackageReference Include="Asp.Versioning.Http" Version="8.*" />
MVC コントローラー用のインストール:
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.*" />
URL セグメントによるバージョニング(推奨)
URL セグメントによるバージョニングでは、バージョンをパス(/api/v1/products)に含めます。最もシンプルな戦略で、すべての HTTP クライアントで利用でき、キャッシュ可能で、ログやドキュメントからもバージョンがはっきりとわかります。
Minimal API
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true; // Adds api-supported-versions header
options.ApiVersionReader = new UrlSegmentApiVersionReader();
});
var app = builder.Build();
var versionSet = app.NewApiVersionSet()
.HasApiVersion(new ApiVersion(1, 0))
.HasApiVersion(new ApiVersion(2, 0))
.ReportApiVersions()
.Build();
var v1 = app.MapGroup("/api/v{version:apiVersion}/products")
.WithApiVersionSet(versionSet)
.MapToApiVersion(new ApiVersion(1, 0));
var v2 = app.MapGroup("/api/v{version:apiVersion}/products")
.WithApiVersionSet(versionSet)
.MapToApiVersion(new ApiVersion(2, 0));
// V1: returns basic product info
v1.MapGet("/", async (AppDbContext db) =>
TypedResults.Ok(await db.Products
.Select(p => new ProductV1Dto(p.Id, p.Name, p.Price))
.ToListAsync()));
// V2: returns extended product info with category
v2.MapGet("/", async (AppDbContext db) =>
TypedResults.Ok(await db.Products
.Select(p => new ProductV2Dto(p.Id, p.Name, p.Price, p.Category, p.CreatedAt))
.ToListAsync()));
MVC コントローラー
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
})
.AddMvc()
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV"; // e.g., v1, v2
options.SubstituteApiVersionInUrl = true;
});
// V1 controller
[ApiController]
[Route("api/v{version:apiVersion}/products")]
[ApiVersion("1.0")]
public sealed class ProductsController(AppDbContext db) : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetAll() =>
Ok(await db.Products
.Select(p => new ProductV1Dto(p.Id, p.Name, p.Price))
.ToListAsync());
}
// V2 controller -- use explicit route, not [controller] token
[ApiController]
[Route("api/v{version:apiVersion}/products")]
[ApiVersion("2.0")]
public sealed class ProductsV2Controller(AppDbContext db) : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetAll() =>
Ok(await db.Products
.Select(p => new ProductV2Dto(p.Id, p.Name, p.Price, p.Category, p.CreatedAt))
.ToListAsync());
}
ヘッダーによるバージョニング
ヘッダーによるバージョニングでは、カスタムリクエストヘッダーから API バージョンを読み取ります。URL を簡潔に保てますが、見つけにくく、ブラウザーからテストするのも困難です。
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new HeaderApiVersionReader("X-Api-Version");
});
クライアントリクエスト:
GET /api/products HTTP/1.1
Host: api.example.com
X-Api-Version: 2.0
クエリ文字列によるバージョニング
クエリ文字列によるバージョニングでは、クエリパラメーター(既定値:api-version)を使用します。使いやすい一方で、URL が煩雑になり、キャッシュ戦略と競合する場合があります。
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new QueryStringApiVersionReader("api-version");
});
クライアントリクエスト:
GET /api/products?api-version=2.0 HTTP/1.1
Host: api.example.com
バージョンリーダーの組み合わせ
複数のリーダーを組み合わせることができます。バージョンを解決した最初のリーダーが採用されます。これは、ある戦略から別の戦略へ移行する際に便利です。
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("X-Api-Version"),
new QueryStringApiVersionReader("api-version"));
サンセットポリシー
サンセットポリシーは、API バージョンが非推奨であり、削除される予定であることを利用者に伝えます。Sunset HTTP レスポンスヘッダーは、RFC 8594 に準拠しています。
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(2, 0);
options.ReportApiVersions = true;
options.Policies.Sunset(1.0)
.Effective(new DateTimeOffset(2026, 6, 1, 0, 0, 0, TimeSpan.Zero))
.Link("https://docs.example.com/api/migration-v1-to-v2")
.Title("V1 to V2 Migration Guide")
.Type("text/html");
});
v1 リクエストに対するレスポンスヘッダー:
api-supported-versions: 1.0, 2.0
api-deprecated-versions: 1.0
Sunset: Sun, 01 Jun 2026 00:00:00 GMT
Link: <https://docs.example.com/api/migration-v1-to-v2>; rel="sunset"; title="V1 to V2 Migration Guide"; type="text/html"
バージョンの非推奨化
バージョンセット(Minimal API)または属性(MVC)を使って、バージョンを非推奨としてマークします。
// Minimal APIs
var versionSet = app.NewApiVersionSet()
.HasApiVersion(new ApiVersion(1, 0))
.HasDeprecatedApiVersion(new ApiVersion(1, 0))
.HasApiVersion(new ApiVersion(2, 0))
.ReportApiVersions()
.Build();
// MVC controllers
[ApiVersion("1.0", Deprecated = true)]
[ApiVersion("2.0")]
public sealed class ProductsController : ControllerBase { }
レガシーパッケージからの移行
Microsoft.AspNetCore.Mvc.Versioning を使用しているプロジェクトは、Asp.Versioning.Mvc(Minimal API の場合は Asp.Versioning.Http)に移行してください。名前空間を変更すれば、API の大部分はそのまま使用できます。
| レガシー名前空間 | 現行の名前空間 |
|---|---|
Microsoft.AspNetCore.Mvc.Versioning | Asp.Versioning |
Microsoft.AspNetCore.Mvc.ApiExplorer | Asp.Versioning.ApiExplorer |
主な移行手順:
- NuGet パッケージ参照を置き換えます
usingディレクティブをMicrosoft.AspNetCore.Mvc.VersioningからAsp.Versioningに更新します- サービス登録を
services.AddApiVersioning()(レガシー拡張メソッド)から、Asp.Versioningの現行拡張メソッドに更新します - カスタムの
IApiVersionReader実装に破壊的変更がないか確認します
詳しい手順については、移行ガイドを参照してください。
バージョン戦略の選択ガイド
| 戦略 | メリット | デメリット | 最適な用途 |
|---|---|---|---|
URL セグメント (/api/v1/) | シンプルで視認性が高く、キャッシュ可能で、あらゆる環境で動作します | バージョンごとに URL が変わります | 公開 API、ほとんどのプロジェクト(推奨) |
ヘッダー (X-Api-Version: 1.0) | URL がすっきりし、パスを変更する必要がありません | 発見しにくく、テストが難しくなります | クライアントを管理できる内部 API |
クエリ文字列 (?api-version=1.0) | 追加が簡単で、パスを変更する必要がありません | URL が煩雑になり、キャッシュキーの問題が発生します | 手早いプロトタイピング、レガシーとの互換性 |
推奨事項: 新規プロジェクトでは、まず URL セグメントによるバージョン管理を採用してください。既存の戦略から移行する場合や、特定のクライアントの制約がある場合にのみ、ヘッダーまたはクエリ文字列の読み取り機能を追加してください。
エージェントでの注意点
- 新規プロジェクトではレガシーの
Microsoft.AspNetCore.Mvc.Versioningパッケージを使用しないでください --Asp.Versioning.Http(Minimal API)またはAsp.Versioning.Mvc(MVC コントローラー)を使用してください。 - パッケージ参照でバージョン番号をハードコードしないでください -- バージョン範囲(例:
8.*)を使用し、パッケージのバージョンが最新の互換性のあるリリースと一致するようにしてください。 ReportApiVersions = trueを忘れないでください -- これを指定しないと、クライアントはレスポンスヘッダーから利用可能なバージョンを確認できません。MapToApiVersionとルートグループのプレフィックスを一貫性なく混在させないでください -- 各ルートグループの対象にする API バージョンは必ず 1 つにしてください。- サンセットポリシーなしでバージョンを非推奨にしないでください -- 利用者が計画を立てられるよう、必ずサンセット日と移行リンクを提示してください。
- 公開 API では
AssumeDefaultVersionWhenUnspecified = trueを使用しないでください -- 利用者に必要なバージョン指定が見えなくなります。代わりに、明示的なバージョン選択を必須にしてください。
前提条件
- .NET 8.0+(LTS の基準バージョン)
- Minimal API 用の
Asp.Versioning.Http - API Explorer との統合を行う MVC コントローラー用の
Asp.Versioning.Mvc.ApiExplorer