次の方法で共有


OpenAPI ドキュメントを生成する

この Microsoft.AspNetCore.OpenApi パッケージは、ASP.NET Core での OpenAPI ドキュメント生成の組み込みサポートを提供します。 このパッケージでは、次の機能が提供されます。

  • 実行時の OpenAPI ドキュメントの生成と、アプリケーション上のエンドポイントを介したアクセスのサポート
  • 生成されたドキュメントを変更できる "transformer" API のサポート。
  • 1 つのアプリから複数の OpenAPI ドキュメントを生成するためのサポート。
  • System.Text.Json によって提供される JSON スキーマのサポートを利用します。
  • ネイティブ AoT と互換性があります。

パッケージ インストール

Microsoft.AspNetCore.OpenApi パッケージのインストール:

パッケージ マネージャー コンソールから、次のコマンドを実行します。

Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease

ビルド時に OpenAPI ドキュメントを生成するためのサポートを追加するには、Microsoft.Extensions.ApiDescription.Server パッケージをインストールします。

パッケージ マネージャー コンソールから、次のコマンドを実行します。

Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease

OpenAPI ドキュメント生成を構成する

コード例を次に示します。

  • OpenAPI サービスを追加します。
  • JSON 形式で OpenAPI ドキュメントを表示するためのエンドポイントを有効にします。
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

アプリを起動し、https://localhost:<port>/openapi/v1.json に移動して、生成された OpenAPI ドキュメントを表示します。

ASP.NET Web アプリに OpenAPI メタデータを含む

エンドポイントの OpenAPI メタデータを含める

ASP.NET は、Web アプリのエンドポイントからメタデータを収集し、それを使用して OpenAPI ドキュメントを生成します。 コントローラーベースのアプリでは、メタデータは [EndpointDescription][HttpPost][Produces] などの属性から収集されます。 Minimal API では、メタデータは属性から収集できますが、ルート ハンドラーから TypedResults を返すなどの拡張メソッドやその他の戦略を使用して設定することもできます。 次の表に、収集されるメタデータと、それを設定するための戦略の概要を示します。

Metadata Attribute 拡張メソッド その他の戦略
まとめ [EndpointSummary] WithSummary
description [EndpointDescription] WithDescription
tags [Tags] WithTags
operationId [EndpointName] WithName
parameters [FromQuery][FromRoute]、、 [FromHeader][FromForm]
パラメーターの説明 [EndpointDescription]
requestBody [FromBody] Accepts
responses [Produces] Produces, ProducesProblem TypedResults
エンドポイントの除外 [ExcludeFromDescription], [ApiExplorerSettings] ExcludeFromDescription

ASP.NET Core では、XML ドキュメントのコメントからメタデータは収集されません。

次のセクションでは、生成された OpenAPI ドキュメントをカスタマイズするためにアプリにメタデータを含める方法を示します。

要約と説明

エンドポイントの概要と説明は、[EndpointSummary] 属性と [EndpointDescription] 属性を使用して、または Minimal API で、WithSummaryWithDescription 拡張メソッドを使用して設定できます。

次のサンプルでは、概要と説明を設定するためのさまざまな方法を示します。

属性は app.MapGet メソッドではなくデリゲート メソッドに配置されることに注意してください。

app.MapGet("/extension-methods", () => "Hello world!")
  .WithSummary("This is a summary.")
  .WithDescription("This is a description.");

app.MapGet("/attributes",
  [EndpointSummary("This is a summary.")]
  [EndpointDescription("This is a description.")]
  () => "Hello world!");

tags

OpenAPI では、分類の形式として各エンドポイントでタグを指定することをサポートしています。

Minimal API では、[Tags] 属性または WithTags 拡張メソッドを使用してタグを設定できます。

次のサンプルでは、タグを設定するためのさまざまな戦略を示します。

app.MapGet("/extension-methods", () => "Hello world!")
  .WithTags("todos", "projects");

app.MapGet("/attributes",
  [Tags("todos", "projects")]
  () => "Hello world!");

operationId

OpenAPI では、操作の一意の識別子または名前として、各エンドポイントの operationId がサポートされています。

Minimal API では、[EndpointName] 属性または WithName 拡張メソッドを使用して operationId を設定できます。

次のサンプルでは、タグを設定するためのさまざまな方法を示します。

app.MapGet("/extension-methods", () => "Hello world!")
  .WithName("FromExtensionMethods");

app.MapGet("/attributes",
  [EndpointName("FromAttributes")]
  () => "Hello world!");

parameters

OpenAPI では、API によって使用されるパス、クエリ文字列、ヘッダー、cookie パラメーターの注釈付けをサポートします。

フレームワークは、ルート ハンドラーのシグネチャに基づいて、要求パラメーターの型を自動的に推論します。

[EndpointDescription] 属性を使用して、パラメーターの説明を指定できます。

次のサンプルでは、パラメーターの説明を設定する方法を示します。

app.MapGet("/attributes",
  ([Description("This is a description.")] string name) => "Hello world!");

要求本文について説明する

OpenAPI のフィールドには requestBody 、サポートされているコンテンツ タイプや本文コンテンツのスキーマなど、API クライアントがサーバーに送信できる要求の本文が記述されています。

エンドポイント ハンドラー メソッドが要求本文からバインドされたパラメーターを受け入れると、ASP.NET Core によって OpenAPI ドキュメント内の操作に対応する requestBody パラメーターが生成されます。 要求本文のメタデータは、属性または拡張メソッドを使用して指定することもできます。 ドキュメント トランスフォーマーまたは操作トランスフォーマーを使用して、追加のメタデータを設定できます。

エンドポイントが要求本文にバインドされたパラメーターを定義せず、代わりに直接要求本文 HttpContext を使用する場合、ASP.NET Core には要求本文のメタデータを指定するメカニズムが用意されています。 これは、要求本文をストリームとして処理するエンドポイントの一般的なシナリオです。

一部の要求本文メタデータは、ルート ハンドラー メソッドのパラメーターからFromBodyFromForm決定できます。

要求本文の説明は、パラメーター FromBody FromFormの属性を使用[Description]して設定できます。

パラメーターが FromBody null 非許容EmptyBodyBehaviorで、属性にFromBody設定Allowされていない場合は、要求本文が必要でありrequired、生成された OpenAPI ドキュメントでフィールドrequestBodyが設定trueされます。 フォーム本体は常に必須であり、次に設定されていますrequiredtrue

ドキュメント トランスフォーマーまたは操作トランスフォーマー使用して、examples、またはencodingフィールドを設定exampleしたり、生成された OpenAPI ドキュメントの要求本文の仕様拡張を追加したりします。

要求本文のメタデータを設定するためのその他のメカニズムは、開発するアプリの種類によって異なります。以降のセクションで説明します。

生成された OpenAPI ドキュメント内の要求本文のコンテンツ タイプは、要求本文にバインドされている、または拡張メソッドで Accepts 指定されたパラメーターの型から決定されます。 既定では、パラメーターのFromBodyコンテンツ タイプは次の値になりapplication/json、パラメーターのコンテンツ タイプFromFormは次のいずれかapplication/x-www-form-urlencodedになりますmultipart/form-data

これらの既定のコンテンツ タイプのサポートは最小限の API に組み込まれており、その他のコンテンツ タイプはカスタム バインドを使用して処理できます。 詳細については、最小限の API ドキュメントのカスタム バインド に関するトピックを参照してください。

要求本文に異なるコンテンツ タイプを指定するには、いくつかの方法があります。 パラメーターの型が FromBody 実装 IEndpointParameterMetadataProviderされている場合、ASP.NET Core はこのインターフェイスを使用して、要求本文のコンテンツ タイプを決定します。 フレームワークでは、このインターフェイスのメソッドを PopulateMetadata 使用して、要求本文の本文コンテンツのコンテンツ タイプと種類を設定します。 たとえば、どちらかapplication/xmlまたはtext/xmlコンテンツ タイプをTodo受け入れるクラスは、フレームワークにこの情報を提供するために使用IEndpointParameterMetadataProviderできます。

public class Todo : IEndpointParameterMetadataProvider
{
    public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
    {
        builder.Metadata.Add(new AcceptsMetadata(["application/xml", "text/xml"], typeof(Todo)));
    }
}

拡張メソッドを Accepts 使用して、要求本文のコンテンツ タイプを指定することもできます。 次の例では、エンドポイントは要求本文で Todo オブジェクトを受け入れ、想定されるコンテンツ タイプは application/xml となります。

app.MapPut("/todos/{id}", (int id, Todo todo) => ...)
  .Accepts<Todo>("application/xml");

組み込みのコンテンツ タイプではないため application/xmlTodo クラスはインターフェイスを IBindableFromHttpContext<TSelf> 実装して、要求本文のカスタム バインディングを提供する必要があります。 次に例を示します。

public class Todo : IBindableFromHttpContext<Todo>
{
    public static async ValueTask<Todo?> BindAsync(HttpContext context, ParameterInfo parameter)
    {
        var xmlDoc = await XDocument.LoadAsync(context.Request.Body, LoadOptions.None, context.RequestAborted);
        var serializer = new XmlSerializer(typeof(Todo));
        return (Todo?)serializer.Deserialize(xmlDoc.CreateReader());
    }

エンドポイントで要求本文にバインドされているパラメーターが定義されていない場合は、拡張メソッドを Accepts 使用して、エンドポイントが受け入れるコンテンツ タイプを指定します。

AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepts%2A> を複数回指定<した場合、最後のメタデータのみが使用されます。これらは結合されません。

応答の種類の説明

OpenAPI では、API から返される応答の記述を提供できます。 ASP.NET Core には、エンドポイントの応答メタデータを設定するためのいくつかの戦略が用意されています。 設定できる応答メタデータには、状態コード、応答本文の種類、応答のコンテンツ タイプが含まれます。 OpenAPI の応答には、説明、ヘッダー、リンク、例などの追加のメタデータが含まれる場合があります。 この追加メタデータは、ドキュメント トランスフォーマーまたは操作トランスフォーマーで設定できます。

応答メタデータを設定するための具体的なメカニズムは、開発するアプリの種類によって異なります。

最小 API アプリでは、ASP.NET Core では、エンドポイントの拡張メソッドによって追加された応答メタデータ、ルート ハンドラーの属性、およびルート ハンドラーの戻り値の型を抽出できます。

  • Producesエンドポイントで拡張メソッドを使用して、状態コード、応答本文の種類、およびエンドポイントからの応答のコンテンツ タイプを指定できます。
  • [ProducesResponseType]またはProducesResponseTypeAttribute<T>属性を使用して、応答本文の型を指定できます。
  • ルート ハンドラーを使用して、応答本文の型とコンテンツ タイプを指定するために実装 IEndpointMetadataProvider する型を返すことができます。
  • ProducesProblemエンドポイントの拡張メソッドを使用して、エラー応答の状態コードとコンテンツ タイプを指定できます。

ProducesメソッドとProducesProblem拡張メソッドは、両方RouteHandlerBuilderでサポートされていることに注意してくださいRouteGroupBuilder。 これにより、たとえば、グループ内のすべての操作に対して一般的な一連のエラー応答を定義できます。

上記のいずれかの方法で指定されていない場合、次のようになります。

  • 応答の状態コードの既定値は 200 です。
  • 応答本文のスキーマは、エンドポイント メソッドの暗黙的または明示的な戻り値の型から推論できます。たとえば、in Task<TResult>からT。それ以外の場合は、指定されていないと見なされます。
  • 指定または推論された応答本文のコンテンツタイプは "application/json" です。

Minimal API では、 Produces 拡張メソッドと属性によって [ProducesResponseType] エンドポイントの応答メタデータのみが設定されます。 エンドポイントの動作は変更または制約されません。エンドポイントの動作は、メタデータで指定された状態コードまたは応答本文の種類とは異なる場合があり、コンテンツ タイプは、属性または拡張メソッドで指定されたコンテンツ タイプに関係なく、ルート ハンドラー メソッドの戻り値の型によって決定されます。

拡張メソッドでは Produces 、エンドポイントの応答の種類を指定できます。既定の状態コードは 200 で、既定のコンテンツ タイプは application/json. 次に例を示します。

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
  .Produces<IList<Todo>>();

エンドポイント [ProducesResponseType] に応答メタデータを追加するために使用できます。 次の例に示すように、ルートを作成するためのメソッド呼び出しではなく、ルート ハンドラー メソッドに属性が適用されることに注意してください。

app.MapGet("/todos",
    [ProducesResponseType<List<Todo>>(200)]
    async (TodoDb db) => await db.Todos.ToListAsync());

エンドポイントのルート ハンドラーの実装で TypedResults を使用すると、エンドポイントに対する応答の型のメタデータが自動的に含まれます。 たとえば、以下のコードは、application/json コンテンツ タイプでの 200 状態コードの応答で、エンドポイントに自動的に注釈を付けます。

app.MapGet("/todos", async (TodoDb db) =>
{
    var todos = await db.Todos.ToListAsync();
    return TypedResults.Ok(todos);
});

OpenAPI ドキュメントにエントリの作成をresponses実装IEndpointMetadataProviderする型のみを返します。 エントリを生成するヘルパー メソッドの一部の一覧を TypedResults 次に responses 示します。

TypedResults ヘルパー メソッド 状態コード
Ok() 200
Created() 201
CreatedAtRoute() 201
Accepted() 202
AcceptedAtRoute() 202
NoContent() 204
BadRequest() 400
ValidationProblem() 400
NotFound() 404
Conflict() 409
UnprocessableEntity() 422

これらのメソッドはすべて、応答本文の型を指定するジェネリック オーバーロードを除きます NoContent

エンドポイント メタデータを設定し、ルート ハンドラーから返すクラスを実装できます。

ProblemDetails の応答を設定する

ProblemDetails 応答を返す可能性があるエンドポイントの応答の種類を設定する場合は、次を使用してエンドポイントに適切な応答メタデータを追加できます。

ProblemDetails 応答を返すように最小限の API アプリを構成する方法の詳細については、「最小限の API でエラーを処理する」を参照してください

複数応答の種類

エンドポイントがさまざまなシナリオでさまざまな種類の応答を返すことができる場合は、次の方法でメタデータを指定できます。

  • 次の例に示すように、Produces 拡張メソッドを複数回呼び出します。

    app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
             await db.Todos.FindAsync(id) 
             is Todo todo
             ? Results.Ok(todo) 
             : Results.NotFound())
       .Produces<Todo>(StatusCodes.Status200OK)
       .Produces(StatusCodes.Status404NotFound);
    
  • 次の例に示すように、シグネチャで Results<TResult1,TResult2,TResult3,TResult4,TResult5,TResult6> を、ハンドラーの本体で TypedResults を使用します。

    app.MapGet("/book/{id}", Results<Ok<Book>, NotFound> (int id, List<Book> bookList) =>
    {
        return bookList.FirstOrDefault((i) => i.Id == id) is Book book
         ? TypedResults.Ok(book)
         : TypedResults.NotFound();
    });
    

    Results<TResult1,TResult2,TResultN> 共用体型は、ルート ハンドラーが IResult を実装する複数の具象型を返し、IEndpointMetadataProvider を実装するそれらの型はいずれもエンドポイントのメタデータに寄与することを宣言します。

    共用体型は、暗黙的なキャスト演算子を実装します。 これらの演算子により、コンパイラはジェネリック引数で指定されている型を共用体型のインスタンスに自動的に変換できるようになります。 この機能にはさらに、ルート ハンドラーからそうすることが宣言されている結果のみが返されることがコンパイル時にチェックされるという利点があります。 ジェネリック引数の 1 つとして宣言されていない型を Results<TResult1,TResult2,TResultN> に返そうとすると、コンパイル エラーが発生します。

生成されたドキュメントからのエンドポイントの除外

既定では、アプリで定義されているすべてのエンドポイントは生成された OpenAPI ファイルに文書化されますが、エンドポイントは属性または拡張メソッドを使用してドキュメントから除外できます。

除外する必要があるエンドポイントを指定するメカニズムは、開発するアプリの種類によって異なります。

最小限の API では、OpenAPI ドキュメントから特定のエンドポイントを除外するための 2 つの戦略がサポートされています。

次のサンプルでは、生成された OpenAPI ドキュメントから指定されたエンドポイントを除外するためのさまざまな方法を示します。

app.MapGet("/extension-method", () => "Hello world!")
  .ExcludeFromDescription();

app.MapGet("/attributes",
  [ExcludeFromDescription]
  () => "Hello world!");

データ型の OpenAPI メタデータを含める

要求または応答本文で使用される C# クラスまたはレコードは、生成された OpenAPI ドキュメントのスキーマとして表されます。 既定では、スキーマではパブリック プロパティのみが表されますが、フィールドのスキーマ プロパティも作成する JsonSerializerOptions があります。

PropertyNamingPolicy がキャメルケースに設定されている場合 (これは ASP.NET Web アプリケーションの既定値です)、スキーマ内のプロパティ名は、クラスまたはレコードプロパティ名のキャメルケース形式です。 [JsonPropertyName] は、個々のプロパティでスキーマ内のプロパティの名前を指定するために使用できます。

種類と形式

JSON スキーマ ライブラリは、標準の C# 型を OpenAPI typeformat に次のようにマップします。

C# 型 OpenAPI type OpenAPI format
int integer int32
long integer int64
short integer int16
byte integer uint8
float 数値 float
double 数値 倍精度浮動小数点
小数 数値 倍精度浮動小数点
bool boolean
string string
char string char
byte[] string byte
DateTimeOffset string date-time
DateOnly string 日付
TimeOnly string time
Uri string uri
GUID string uuid
オブジェクト 省略
動的 省略

オブジェクト型と動的型には、no 型が OpenAPI で定義されていることに注意してください。これは、int や string などのプリミティブ型を含む任意の型のデータを含むことができるためです。

typeformat は、スキーマ トランスフォーマーで設定することもできます。 たとえば、10 進型の formatdouble の代わりに decimal にするとします。

属性を使用してメタデータを追加する

ASP.NET は、クラスプロパティまたはレコードプロパティの属性のメタデータを使用して、生成されたスキーマの対応するプロパティにメタデータを設定します。

次の表は、生成されたスキーマのメタデータを提供する System.ComponentModel 名前空間の属性をまとめたものです。

属性 説明
[Description] スキーマ内のプロパティの description を設定します。
[Required] プロパティをスキーマ内の required としてマークします。
[DefaultValue] スキーマ内のプロパティの default 値を設定します。
[Range] 整数または数値の minimummaximum の値を設定します。
[MinLength] 文字列の minLength を設定します。
[MaxLength] 文字列の maxLength を設定します。
[RegularExpression] 文字列の pattern を設定します。

コントローラー ベースのアプリでは、これらの属性によってフィルターが操作に追加され、入力データが制約を満たしていることに注意します。 最小限の API では、これらの属性によって生成されたスキーマのメタデータが設定されますが、検証は、エンドポイント フィルター、ルート ハンドラーのロジック、またはサード パーティのパッケージを介して明示的に実行する必要があります。

生成されたスキーマのメタデータのその他のソース

必須

プロパティは、required 修飾子を使用して required としてマークすることもできます。

enum

C# の列挙型は整数ベースですが、JSON では [JsonConverter]JsonStringEnumConverter を使用して文字列として表すことができます。 列挙型が JSON で文字列として表される場合、生成されるスキーマには列挙型の文字列値を持つ enum プロパティが含まれます。 [JsonConverter] のない列挙型は、生成されたスキーマで type: integer として定義されます。

注: [AllowedValues] はプロパティの enum 値を設定しません。

nullable

null 許容値または参照型として定義されたプロパティは、生成されたスキーマに nullable: true があります。 これは、Nullable プロパティの有効な値として null を受け入れる、System.Text.Json 逆シリアライザーの既定の動作と一致します。

additionalProperties

既定では、スキーマは additionalProperties アサーションなしで生成されます。これは、true の既定値を意味します。 これは、System.Text.Json 逆シリアライザーの既定の動作と一致し、JSON オブジェクト内の追加のプロパティは自動的に無視されます。

スキーマの追加プロパティに特定の型の値のみを含める必要がある場合は、プロパティまたはクラスを Dictionary<string, type> として定義します。 ディクショナリのキーの種類は string である必要があります。 これにより、必要な値型として "type" のスキーマを指定する additionalProperties を持つスキーマが生成されます。

ポリモーフィック型のメタデータ

親クラスの [JsonPolymorphic] 属性と [JsonDerivedType] 属性を使用して、ポリモーフィック型の識別子フィールドとサブタイプを指定します。

[JsonDerivedType] は、サブクラスの特定の識別子値を指定する列挙型を使用して、各サブクラスのスキーマに識別子フィールドを追加します。 この属性は、識別子の値を設定するために、各派生クラスのコンストラクターも変更します。

[JsonPolymorphic] 属性を持つ抽象クラスにはスキーマに discriminator フィールドがありますが、[JsonPolymorphic] 属性を持つ具象クラスには discriminator フィールドがありません。 OpenAPI では、識別子プロパティをスキーマの必須プロパティにする必要がありますが、識別子プロパティは具象基底クラスで定義されていないため、スキーマに discriminator フィールドを含めることはできません。

スキーマ トランスフォーマーを使用したメタデータの追加

スキーマ トランスフォーマーを使用すると、既定のメタデータをオーバーライドしたり、example 値などのメタデータを生成されたスキーマに追加したりできます。 詳細については、「スキーマ トランスフォーマーの使用」をご覧ください。

OpenAPI ドキュメント生成をカスタマイズするためのオプション

次のセクションでは、OpenAPI ドキュメントの生成をカスタマイズする方法について説明します。

OpenAPI ドキュメント名をカスタマイズする

アプリ内の各 OpenAPI ドキュメントには一意の名前があります。 登録されている既定のドキュメント名は v1 です。

builder.Services.AddOpenApi(); // Document name is v1

ドキュメント名は、AddOpenApi 呼び出しに名前をパラメーターとして渡すことによって変更できます。

builder.Services.AddOpenApi("internal"); // Document name is internal

ドキュメント名は、OpenAPI 実装内のさまざまな場所に現れます。

生成された OpenAPI ドキュメントをフェッチすると、ドキュメント名が要求の documentName パラメーター引数として指定されます。 次の要求は、v1internal ドキュメントを解決します。

GET http://localhost:5000/openapi/v1.json
GET http://localhost:5000/openapi/internal.json

生成されたドキュメントの OpenAPI バージョンをカスタマイズする

既定では、OpenAPI ドキュメント生成によって、v3.0 の OpenAPI 仕様に準拠したドキュメントが生成されます。 次のコードは、OpenAPI ドキュメントの既定のバージョンを変更する方法を示しています。

builder.Services.AddOpenApi(options =>
{
    options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0;
});

OpenAPI エンドポイント ルートをカスタマイズする

既定では、MapOpenApi への呼び出しによって登録された OpenAPI エンドポイントは、/openapi/{documentName}.json エンドポイントでドキュメントを公開します。 次のコードは、OpenAPI ドキュメントを登録するルートをカスタマイズする方法を示しています。

app.MapOpenApi("/openapi/{documentName}/openapi.json");

注: エンドポイント ルートから documentName ルート パラメーターを削除することは可能ですが、お勧めしません。 documentName ルート パラメーターがエンドポイント ルートから削除されると、フレームワークはクエリ パラメーターからドキュメント名の解決を試みます。 ルートにもクエリにも documentName が指定されてないと、予期しない動作が発生する可能性があります。

OpenAPI エンドポイントをカスタマイズする

OpenAPI ドキュメントはルート ハンドラー エンドポイントを介して提供されるため、標準の最小エンドポイントで使用できるカスタマイズは、OpenAPI エンドポイントで使用できます。

OpenAPI ドキュメントへのアクセスを許可されているユーザーに制限する

OpenAPI エンドポイントでは、既定では認可チェックは有効になりません。 ただし、承認チェックは OpenAPI ドキュメントに適用できます。 たとえば、次のコードでは、OpenAPI ドキュメントへのアクセスは、tester ロールを持つユーザーに制限されています。

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization(o =>
{
    o.AddPolicy("ApiTesterPolicy", b => b.RequireRole("tester"));
});
builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi()
    .RequireAuthorization("ApiTesterPolicy");

app.MapGet("/", () => "Hello world!");

app.Run();

生成された OpenAPI ドキュメントをキャッシュする

OpenAPI ドキュメントは、OpenAPI エンドポイントへの要求が送信されるたびに再生成されます。 再生成により、トランスフォーマーは動的なアプリケーション状態を操作に組み込むことができます。 たとえば、HTTP コンテキストの詳細を使用して要求を再生成します。 必要に応じて、OpenAPI ドキュメントをキャッシュして、各 HTTP 要求でドキュメント生成パイプラインが実行されないようにすることができます。

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(policy => policy.Expire(TimeSpan.FromMinutes(10)));
});
builder.Services.AddOpenApi();

var app = builder.Build();

app.UseOutputCache();

app.MapOpenApi()
    .CacheOutput();

app.MapGet("/", () => "Hello world!");

app.Run();

OpenAPI ドキュメントのトランスフォーマー

このセクションでは、トランスフォーマーを使用して OpenAPI ドキュメントをカスタマイズする方法について説明します。

トランスフォーマーを使用して OpenAPI ドキュメントをカスタマイズする

トランスフォーマーは、ユーザー定義のカスタマイズによって OpenAPI ドキュメントを変更するための API を提供します。 トランスフォーマーは、次のようなシナリオに役立ちます。

  • ドキュメント内のすべての操作にパラメーターを追加する。
  • パラメーターまたは操作の説明を変更する。
  • OpenAPI ドキュメントに最上位の情報を追加する。

トランスフォーマーは、次の 3 つのカテゴリに分類されます。

  • ドキュメント トランスフォーマーは、OpenAPI ドキュメント全体にアクセスできます。 これらを使用して、ドキュメントをグローバルに変更できます。
  • 操作トランスフォーマーは、個々の操作に適用されます。 個々の各操作は、パスと HTTP メソッドの組み合わせです。 これらを使用して、エンドポイントのパラメーターまたは応答を変更できます。
  • スキーマ トランスフォーマーは、ドキュメント内の各スキーマに適用されます。 これらは、要求または応答本文のスキーマ、または入れ子になったスキーマを変更するために使用できます。

トランスフォーマーは、OpenApiOptions オブジェクトの AddDocumentTransformer メソッドを呼び出してドキュメントに登録できます。 次のスニペットは、トランスフォーマーをドキュメントに登録するさまざまな方法を示しています。

  • デリゲートを使用してドキュメント トランスフォーマーを登録する。
  • IOpenApiDocumentTransformer のインスタンスを使用してドキュメント トランスフォーマーを登録する。
  • DI によってアクティブ化される IOpenApiDocumentTransformer を使用してドキュメント トランスフォーマーを登録する。
  • デリゲートを使用して操作トランスフォーマーを登録する。
  • IOpenApiOperationTransformer のインスタンスを使用して操作トランスフォーマーを登録する。
  • DI によってアクティブ化される IOpenApiOperationTransformer を使用して操作トランスフォーマーを登録する。
  • デリゲートを使用してスキーマ トランスフォーマーを登録する。
  • IOpenApiSchemaTransformer のインスタンスを使用してスキーマ トランスフォーマーを登録する。
  • DI によってアクティブ化される IOpenApiSchemaTransformer を使用してスキーマ トランスフォーマーを登録する。
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer((document, context, cancellationToken)
                             => Task.CompletedTask);
    options.AddDocumentTransformer(new MyDocumentTransformer());
    options.AddDocumentTransformer<MyDocumentTransformer>();
    options.AddOperationTransformer((operation, context, cancellationToken)
                            => Task.CompletedTask);
    options.AddOperationTransformer(new MyOperationTransformer());
    options.AddOperationTransformer<MyOperationTransformer>();
    options.AddSchemaTransformer((schema, context, cancellationToken)
                            => Task.CompletedTask);
    options.AddSchemaTransformer(new MySchemaTransformer());
    options.AddSchemaTransformer<MySchemaTransformer>();
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

トランスフォーマーの実行順序

トランスフォーマーは、登録に基づいて先入れ先出し順に実行されます。 次のスニペットでは、ドキュメント トランスフォーマーは、操作トランスフォーマーによって行われた変更にアクセスできます。

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddOperationTransformer((operation, context, cancellationToken)
                                     => Task.CompletedTask);
    options.AddDocumentTransformer((document, context, cancellationToken)
                                     => Task.CompletedTask);
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

ドキュメント トランスフォーマーを使用する

ドキュメント トランスフォーマーは、次を含むコンテキスト オブジェクトにアクセスできます。

  • 変更するドキュメントの名前。
  • その ApiDescriptionGroups ドキュメントに関連付けられているもの。
  • ドキュメントの生成で使用される IServiceProvider

ドキュメント トランスフォーマーは、生成された OpenAPI ドキュメントを変化させることもできます。 次の例は、API に関する情報を OpenAPI ドキュメントに追加するドキュメント トランスフォーマーを示しています。

using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer((document, context, cancellationToken) =>
    {
        document.Info = new()
        {
            Title = "Checkout API",
            Version = "v1",
            Description = "API for processing checkouts from cart."
        };
        return Task.CompletedTask;
    });
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

サービスによってアクティブ化されたドキュメント トランスフォーマーは、DI のインスタンスを利用してアプリを変更できます。 次のサンプルでは、認証層から IAuthenticationSchemeProvider サービスを使用するドキュメント トランスフォーマーを示します。 JWT ベアラー関連のスキームがアプリに登録されているかどうかを確認し、OpenAPI ドキュメントの最上位レベルに追加します。

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
    public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
    {
        var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
        if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
        {
            var requirements = new Dictionary<string, OpenApiSecurityScheme>
            {
                ["Bearer"] = new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer", // "bearer" refers to the header name here
                    In = ParameterLocation.Header,
                    BearerFormat = "Json Web Token"
                }
            };
            document.Components ??= new OpenApiComponents();
            document.Components.SecuritySchemes = requirements;
        }
    }
}

ドキュメント トランスフォーマーは、関連付けられているドキュメント インスタンスに固有です。 次の例では、トランスフォーマーは以下を実行します。

  • 認証関連の要件を internal ドキュメントに登録します。
  • public ドキュメントは変更されません。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();

builder.Services.AddOpenApi("internal", options =>
{
    options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
builder.Services.AddOpenApi("public");

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/world", () => "Hello world!")
    .WithGroupName("internal");
app.MapGet("/", () => "Hello universe!")
    .WithGroupName("public");

app.Run();

internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
    public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
    {
        var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
        if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
        {
            // Add the security scheme at the document level
            var requirements = new Dictionary<string, OpenApiSecurityScheme>
            {
                ["Bearer"] = new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer", // "bearer" refers to the header name here
                    In = ParameterLocation.Header,
                    BearerFormat = "Json Web Token"
                }
            };
            document.Components ??= new OpenApiComponents();
            document.Components.SecuritySchemes = requirements;

            // Apply it as a requirement for all operations
            foreach (var operation in document.Paths.Values.SelectMany(path => path.Operations))
            {
                operation.Value.Security.Add(new OpenApiSecurityRequirement
                {
                    [new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }] = Array.Empty<string>()
                });
            }
        }
    }
}

操作トランスフォーマーを使用する

操作は、OpenAPI ドキュメント内の HTTP パスとメソッドの一意の組み合わせです。 操作トランスフォーマーは、次の変更を行うときに役立ちます。

  • アプリ内の各エンドポイントに対して行う必要があります。または
  • 特定のルートに条件付きで適用されます。

操作トランスフォーマーは、次を含むコンテキスト オブジェクトにアクセスできます。

  • 操作が属するドキュメントの名前。
  • 操作に関連付けられた ApiDescription
  • ドキュメントの生成で使用される IServiceProvider

たとえば、次の操作トランスフォーマーは、ドキュメント内のすべての操作でサポートされる応答状態コードとして 500 を追加します。

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddAuthentication().AddJwtBearer();

builder.Services.AddOpenApi(options =>
{
    options.AddOperationTransformer((operation, context, cancellationToken) =>
    {
        operation.Responses.Add("500", new OpenApiResponse { Description = "Internal server error" });
        return Task.CompletedTask;
    });
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => "Hello world!");

app.Run();

スキーマ トランスフォーマーの使用

スキーマは、OpenAPI ドキュメントの要求本文と応答本文で使用されるデータ モデルです。 スキーマ トランスフォーマーは、次の変更を行う場合に便利です。

  • ドキュメント内の各スキーマに対して作成する必要があります。
  • 特定のスキーマに条件付きで適用されます。

スキーマ トランスフォーマーは、次を含むコンテキスト オブジェクトにアクセスできます。

  • スキーマが属するドキュメントの名前。
  • ターゲット スキーマに関連付けられている JSON 型情報。
  • ドキュメントの生成で使用される IServiceProvider

たとえば、次のスキーマ トランスフォーマーは、10 進型の formatdouble ではなく decimal に設定します。

using Microsoft.AspNetCore.OpenApi;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options => {
    // Schema transformer to set the format of decimal to 'decimal'
    options.AddSchemaTransformer((schema, context, cancellationToken) =>
    {
        if (context.JsonTypeInfo.Type == typeof(decimal))
        {
            schema.Format = "decimal";
        }
        return Task.CompletedTask;
    });
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/", () => new Body { Amount = 1.1m });

app.Run();

public class Body {
    public decimal Amount { get; set; }
}

その他のリソース

Minimal API は、Microsoft.AspNetCore.OpenApi パッケージを使ってアプリ内のエンドポイントに関する情報を生成するための組み込みサポートを提供します。 生成された OpenAPI 定義をビジュアル UI で表示するには、サードパーティ製のパッケージが必要です。 コントローラー ベースの API での OpenAPI のサポートについては、この記事の「.NET 9 バージョン」をご覧ください。

次のコードは、ASP.NET Core の最小限の Web API テンプレートによって生成され、OpenAPI が使用されます。

using Microsoft.AspNetCore.OpenApi;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateTime.Now.AddDays(index),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.Run();

internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

前の強調表示されたコードでは、次のようになっています。

  • Microsoft.AspNetCore.OpenApi については、次のセクションで説明します。
  • AddEndpointsApiExplorer : API Explorer を使用して、既定の注釈によってエンドポイントを検出して記述するようにアプリを構成します。 WithOpenApi は、一致する、API Explorer によって生成された既定の注釈を、Microsoft.AspNetCore.OpenApi パッケージから生成された注釈でオーバーライドします。
  • UseSwaggerSwagger ミドルウェアを追加します。
  • `UseSwaggerUI` により、Swagger UI ツールの埋め込みバージョンが有効になります。
  • WithName: エンドポイントの IEndpointNameMetadata はリンク生成に使用され、指定されたエンドポイントの OpenAPI 仕様の操作 ID として扱われます。
  • WithOpenApi については、この記事で後ほど説明します。

Microsoft.AspNetCore.OpenApi NuGet パッケージ

ASP.NET Core では、エンドポイントの OpenAPI 仕様と対話するための Microsoft.AspNetCore.OpenApi パッケージが提供されます。 このパッケージは、Microsoft.AspNetCore.OpenApi パッケージで定義されている OpenAPI モデルと、Minimal API で定義されているエンドポイント間のリンクとして機能します。 このパッケージには、エンドポイントのパラメーター、応答、メタデータを調べて、エンドポイントを記述するために使用される OpenAPI 注釈の種類を構築する API が用意されています。

Microsoft.AspNetCore.OpenApi は PackageReference としてプロジェクト ファイルに追加されます。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>    
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.*-*" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
  </ItemGroup>

</Project>

Microsoft.AspNetCore.OpenApiSwashbuckle.AspNetCore を使用する場合は、Swashbuckle.AspNetCore 6.4.0 以降を使用する必要があります。 WithOpenApi 呼び出しでコピー コンストラクターを活用するには、Microsoft.OpenApi 1.4.3 以降を使用する必要があります。

WithOpenApi によるエンドポイントへの OpenAPI 注釈の追加

エンドポイント上で WithOpenApi を呼び出すと、エンドポイントのメタデータに追加されます。 このメタデータは:

  • Swashbuckle.AspNetCore などのサードパーティ製パッケージで使用できます。
  • Swagger ユーザー インターフェイス、または API を定義するために生成された YAML や JSON で表示されます。
app.MapPost("/todoitems/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi();

WithOpenApi の OpenAPI 注釈を変更する

WithOpenApi メソッドには、OpenAPI 注釈の変更に使用できる関数を指定できます。 たとえば、次のコードでは、エンドポイントの最初のパラメーターに記述が追加されています。

app.MapPost("/todo2/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi(generatedOperation =>
{
    var parameter = generatedOperation.Parameters[0];
    parameter.Description = "The ID associated with the created Todo";
    return generatedOperation;
});

OpenAPI に操作 ID を追加する

操作 ID は、OpenAPI において指定されたエンドポイントを一意に識別するために使われます。 WithName 拡張メソッドを使うと、メソッドに使われる操作 ID を設定できます。

app.MapGet("/todoitems2", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithName("GetToDoItems");

または、OpenAPI 注釈で OperationId プロパティを直接設定することもできます。

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        OperationId = "GetTodos"
    });

OpenAPI の記述にタグを追加する

OpenAPI では、タグ オブジェクトを使った操作の分類をサポートしています。 これらのタグは、通常、Swagger UI で操作をグループ化するために使われます。 必要なタグを持つエンドポイント上で WithTags 拡張メソッドを呼び出すことにより、これらのタグを操作に追加できます。

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithTags("TodoGroup");

または、WithOpenApi 拡張メソッドを使って、OpenApiTags の一覧を OpenAPI 注釈に設定することもできます。

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        Tags = new List<OpenApiTag> { new() { Name = "Todos" } }
    });

エンドポイントの概要または説明を追加する

エンドポイントの概要と説明は、WithOpenApi 拡張メソッドを呼び出すことによって追加できます。 次のコードでは、OpenAPI 注釈に概要を直接設定しています。

app.MapGet("/todoitems2", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        Summary = "This is a summary",
        Description = "This is a description"
    });

OpenAPI の記述を除外する

次のサンプルでは、OpenAPI の説明の生成から /skipme エンドポイントが除外されます。

using Microsoft.AspNetCore.OpenApi;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapGet("/swag", () => "Hello Swagger!")
    .WithOpenApi();
app.MapGet("/skipme", () => "Skipping Swagger.")
                    .ExcludeFromDescription();

app.Run();

API を古いものとしてマークする

エンドポイントを古いものとしてマークするには、OpenAPI 注釈に Deprecated プロパティを設定します。

app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .WithOpenApi(operation => new(operation)
    {
        Deprecated = true
    });

応答の種類の説明

OpenAPI では、API から返される応答の記述を提供できます。 Minimal API は、エンドポイントの応答の型を設定するための 3 つの方法をサポートしています。

  • エンドポイント上の Produces 拡張メソッドによる方法
  • ルート ハンドラーの ProducesResponseType 属性を使う方法
  • ルート ハンドラーから TypedResults を返す方法

Produces 拡張メソッドを使って、エンドポイントに Produces メタデータを追加できます。 パラメーターが指定されていない場合、拡張メソッドは 200 状態コードと application/json コンテンツ タイプの下で対象となる型のメタデータを設定します。

app
    .MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
    .Produces<IList<Todo>>();

エンドポイントのルート ハンドラーの実装で TypedResults を使用すると、エンドポイントに対する応答の型のメタデータが自動的に含まれます。 たとえば、以下のコードは、application/json コンテンツ タイプでの 200 状態コードの応答で、エンドポイントに自動的に注釈を付けます。

app.MapGet("/todos", async (TodoDb db) =>
{
    var todos = await db.Todos.ToListAsync());
    return TypedResults.Ok(todos);
});

ProblemDetails の応答を設定する

ProblemDetails 応答を返す可能性があるエンドポイントの応答の種類を設定する場合、ProducesProblem の拡張メソッドである ProducesValidationProblem、または TypedResults.Problem を使用して、エンドポイントのメタデータに適切な注釈を追加できます。 ProducesProblemProducesValidationProblem の拡張メソッドは、.NET 8 以前のルート グループでは使用できないことに注意してください。

上記のいずれかの方法で明示的な注釈が提供されていない場合、フレームワークは応答のシグネチャを調べることによって、既定の応答の型を決定しようとします。 この既定の応答は、OpenAPI 定義の 200 状態コードの下に設定されます。

複数応答の種類

エンドポイントがさまざまなシナリオでさまざまな種類の応答を返すことができる場合は、次の方法でメタデータを指定できます。

  • 次の例に示すように、Produces 拡張メソッドを複数回呼び出します。

    app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
             await db.Todos.FindAsync(id) 
             is Todo todo
             ? Results.Ok(todo) 
             : Results.NotFound())
       .Produces<Todo>(StatusCodes.Status200OK)
       .Produces(StatusCodes.Status404NotFound);
    
  • 次の例に示すように、シグネチャで Results<TResult1,TResult2,TResultN> を、ハンドラーの本体で TypedResults を使用します。

    app.MapGet("/book/{id}", Results<Ok<Book>, NotFound> (int id, List<Book> bookList) =>
    {
        return bookList.FirstOrDefault((i) => i.Id == id) is Book book
         ? TypedResults.Ok(book)
         : TypedResults.NotFound();
    });
    

    Results<TResult1,TResult2,TResultN> 共用体型は、ルート ハンドラーが IResult を実装する複数の具象型を返し、IEndpointMetadataProvider を実装するそれらの型はいずれもエンドポイントのメタデータに寄与することを宣言します。

    共用体型は、暗黙的なキャスト演算子を実装します。 これらの演算子により、コンパイラはジェネリック引数で指定されている型を共用体型のインスタンスに自動的に変換できるようになります。 この機能にはさらに、ルート ハンドラーからそうすることが宣言されている結果のみが返されることがコンパイル時にチェックされるという利点があります。 ジェネリック引数の 1 つとして宣言されていない型を Results<TResult1,TResult2,TResultN> に返そうとすると、コンパイル エラーが発生します。

要求本文とパラメーターについて記述する

OpenAPI では、エンドポイントによって返される型を記述するだけでなく、API によって使用される入力に注釈を付けることもできます。 これらの入力は、2 つのカテゴリに分類されます。

  • パス、クエリ文字列、ヘッダー、または Cookie に含まれるパラメーター
  • 要求本文の一部として送信されるデータ

フレームワークは、ルート ハンドラーのシグネチャに基づいて、パス、クエリ、ヘッダー文字列の要求パラメーターの型を自動的に推論します。

要求本文として送信される入力の型を定義するには、Accepts 拡張メソッドを使ってプロパティを構成し、要求ハンドラーで想定されるオブジェクトの種類とコンテンツ タイプを定義します。 次の例では、エンドポイントは要求本文で Todo オブジェクトを受け入れ、想定されるコンテンツ タイプは application/xml となります。

app.MapPost("/todos/{id}", (int id, Todo todo) => ...)
  .Accepts<Todo>("application/xml");

Accepts 拡張メソッドに加えて、IEndpointParameterMetadataProvider インターフェイスを実装することで、パラメーター型がそれ自身の注釈を記述できます。 たとえば、次の Todo 型は application/xml コンテンツ タイプを持つ要求本文を必要とする注釈を追加します。

public class Todo : IEndpointParameterMetadataProvider
{
    public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
    {
        builder.Metadata.Add(new ConsumesAttribute(typeof(Todo), isOptional: false, "application/xml"));
    }
}

明示的な注釈が提供されない場合、フレームワークは、エンドポイント ハンドラーに要求本文パラメーターがあれば、既定の要求の型を決定しようとします。 推論では、次のヒューリスティックを使って注釈が生成されます。

  • [FromForm] 属性によってフォームから読み込まれる要求本文パラメーターは、multipart/form-data コンテンツ タイプで記述されます。
  • 他のすべての要求本文パラメーターは application/json コンテンツ タイプで記述されます。
  • 要求本文が Null 許容であるか、AllowEmpty プロパティが FromBody 属性に設定されている場合は、省略可能として扱われます。

API バージョン管理のサポート

Minimal API では、Asp.Versioning.Http パッケージを使った API バージョン管理をサポートしています。 Minimal API でバージョン管理を構成する例については、API バージョン管理リポジトリを参照してください。

GitHub 上の ASP.NET Core OpenAPI ソース コード

その他のリソース

Minimal API アプリは、Swashbuckle を使用してルート ハンドラーの OpenAPI 仕様を記述できます。

コントローラー ベースの API での OpenAPI のサポートについては、この記事の「.NET 9 バージョン」をご覧ください。

次のコードは、OpenAPI をサポートする典型的な ASP.NET Core アプリです。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = builder.Environment.ApplicationName,
                               Version = "v1" });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger(); // UseSwaggerUI Protected by if (env.IsDevelopment())
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
                                    $"{builder.Environment.ApplicationName} v1"));
}

app.MapGet("/swag", () => "Hello Swagger!");

app.Run();

OpenAPI の記述を除外する

次のサンプルでは、OpenAPI の説明の生成から /skipme エンドポイントが除外されます。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(); // UseSwaggerUI Protected by if (env.IsDevelopment())
}

app.MapGet("/swag", () => "Hello Swagger!");
app.MapGet("/skipme", () => "Skipping Swagger.")
                    .ExcludeFromDescription();

app.Run();

応答の種類の説明

次の例は、組み込みの結果の型を使用して応答をカスタマイズします。

app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
         await db.Todos.FindAsync(id) 
         is Todo todo
         ? Results.Ok(todo) 
         : Results.NotFound())
   .Produces<Todo>(StatusCodes.Status200OK)
   .Produces(StatusCodes.Status404NotFound);

OpenAPI に操作 ID を追加する

app.MapGet("/todoitems2", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithName("GetToDoItems");

OpenAPI の記述にタグを追加する

次のコードは、OpenAPI のグループ化タグを使用します。

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync())
    .WithTags("TodoGroup");