OpenAPI-Unterstützung in Minimal-API-Apps

Die OpenAPI-Spezifikation ist ein gegenüber der Programmiersprache agnostischer Standard zum Dokumentieren von HTTP-APIs. Dieser Standard wird in Minimal-APIs über eine Kombination aus integrierten APIs und Open-Source-Bibliotheken unterstützt. Es gibt drei wichtige Aspekte für die OpenAPI-Integration in eine Anwendung:

  • Generieren von Informationen zu den Endpunkten in der App
  • Sammeln der Informationen in einem Format, das dem OpenAPI-Schema entspricht
  • Verfügbarmachen des generierten OpenAPI-Schemas über eine visuelle Benutzeroberfläche oder serialisierte Datei

Minimal-APIs bieten über das Paket Microsoft.AspNetCore.OpenApi integrierte Unterstützung für das Generieren von Informationen zu Endpunkten in einer App. Für das Verfügbarmachen der generierten OpenAPI-Definition über eine visuelle Benutzeroberfläche ist ein Paket eines Drittanbieters erforderlich.

Der folgende Code ist aus der minimalen Web-API-Vorlage von ASP.NET Core generiert und verwendet 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);
}

Im hervorgehobenen Code oben:

  • Microsoft.AspNetCore.OpenApi wird im nächsten Abschnitt erläutert.
  • AddEndpointsApiExplorer : Konfiguriert die App so, dass sie den API-Explorer verwendet, um Endpunkte mit Standardanmerkungen zu ermitteln und zu beschreiben. WithOpenApi setzt die Übereinstimmung von Standardanmerkungen, die vom API-Explorer generiert werden, mit denen, die aus dem Microsoft.AspNetCore.OpenApi Paket erstellt wurden, außer Kraft.
  • UseSwaggerfügt die Swagger Middleware hinzu. Benötigt das Swashbuckle.AspNetCore-NuGet-Paket
  • UseSwaggerUI ermöglicht eine eingebettete Version des Swagger UI-Tools im Entwicklungsmodus.
  • WithName: Der IEndpointNameMetadata auf dem Endpunkt wird für die Linkgenerierung verwendet und wird als Vorgangs-ID in der OpenAPI-Spezifikation des angegebenen Endpunkts behandelt.
  • WithOpenApi wird später im Artikel erläutert.

Microsoft.AspNetCore.OpenApi NuGet-Paket

ASP.NET Core stellt das Microsoft.AspNetCore.OpenApi Paket bereit, um mit OpenAPI-Spezifikationen für Endpunkte zu interagieren. Das Paket verhält sich wie eine Verknüpfung zwischen den OpenAPI-Modellen, die im Microsoft.AspNetCore.OpenApi Paket definiert sind, und den Endpunkten, die in minimalen APIs definiert sind. Das Paket stellt eine API bereit, welche die Parameter, Antworten und Metadaten eines Endpunkts untersucht, um einen OpenAPI-Anmerkungstyp zu erstellen, der zum Beschreiben eines Endpunkts verwendet wird.

Microsoft.AspNetCore.OpenApi wird als PackageReference zu einer Projektdatei hinzugefügt:

<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>

Wenn Swashbuckle.AspNetCore mit Microsoft.AspNetCore.OpenApi verwendet wird, muss Swashbuckle.AspNetCore 6.4.0 oder höher verwendet werden. Microsoft.OpenApi 1.4.3 oder höher ist erforderlich, um Kopierkonstruktoren in WithOpenApi-Aufrufen zu verwenden.

Hinzufügen von OpenAPI-Anmerkungen zu Endpunkten über WithOpenApi

Durch Aufrufen von WithOpenApi für den Endpunkt werden die Metadaten des Endpunkts ergänzt. Diese Metadaten können:

  • In Drittanbieterpaketen wie Swashbuckle.AspNetCore verarbeitet werden.
  • Auf der Benutzeroberfläche von Swagger oder in YAML oder JSON angezeigt werden, die zum Definieren der API erstellt wurden.
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();

Ändern der OpenAPI-Anmerkung in WithOpenApi

Die WithOpenApi Methode akzeptiert eine Funktion, die benutzt werden kann, um die OpenAPI-Anmerkung zu verändern. Im folgenden Code wird beispielsweise eine Beschreibung zum ersten Parameter des Endpunkts hinzugefügt:

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;
});

Hinzufügen von Vorgangs-IDs zu OpenAPI

Vorgangs-IDs werden verwendet, um einen bestimmten Endpunkt in OpenAPI eindeutig zu identifizieren. Mit der Erweiterungsmethode WithName können Sie die für eine Methode verwendete Vorgangs-ID festlegen.

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

Alternativ kann die OperationId-Eigenschaft auch direkt für die OpenAPI-Anmerkung festgelegt werden.

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

Hinzufügen von Tags zur OpenAPI-Beschreibung

OpenAPI unterstützt Tagobjekte zum Kategorisieren von Vorgängen. Diese Tags werden in der Regel zum Gruppieren von Vorgängen auf der Swagger-Benutzeroberfläche verwendet. Diese Tags können einem Vorgang hinzugefügt werden, indem Sie die Erweiterungsmethode WithTags für den Endpunkt mit den gewünschten Tags aufrufen.

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

Alternativ kann die Liste der OpenApiTags über die OpenAPI-Anmerkung mittels der Erweiterungsmethode WithOpenApi festgelegt werden.

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

Hinzufügen einer Endpunktzusammenfassung oder -beschreibung

Die Endpunktzusammenfassung und -beschreibung kann durch Aufrufen der WithOpenApi-Erweiterungsmethode hinzugefügt werden. Im folgenden Code werden die Zusammenfassungen direkt in der OpenAPI-Anmerkung festgelegt.

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

Ausschließen der OpenAPI-Beschreibung

Im folgenden Beispiel wird der /skipme-Endpunkt von der Generierung einer OpenAPI-Beschreibung ausgeschlossen:

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();

Markieren einer API als veraltet

Wenn Sie einen Endpunkt als veraltet markieren möchten, legen Sie für die OpenAPI-Anmerkung die Eigenschaft Deprecated fest.

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

Beschreiben von Antworttypen

OpenAPI unterstützt die Bereitstellung einer Beschreibung der von einer API zurückgegebenen Antworten. Minimal-APIs unterstützen drei Strategien zum Festlegen des Antworttyps eines Endpunkts:

Mit der Erweiterungsmethode Produces können einem Endpunkt Produces-Metadaten hinzugefügt werden. Wenn keine Parameter angegeben werden, füllt die Erweiterungsmethode die Metadaten für den Zieltyp mit dem Statuscode 200 und dem Inhaltstyp application/json auf.

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

Die Verwendung von TypedResults in der Implementierung des Routenhandlers eines Endpunkts schließt automatisch die Metadaten des Antworttyps für den Endpunkt ein. Der nachstehende Code versieht beispielsweise den Endpunkt automatisch mit einer Antwort unter dem Statuscode 200 mit dem Inhaltstyp application/json.

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

Festlegen von Antworten für ProblemDetails

Wenn Sie den Antworttyp für Endpunkte festlegen, die möglicherweise eine Antwort des Typs ProblemDetails zurückgeben, können Sie mit der Erweiterungsmethode ProducesProblem oder TypedResults.Problem die entsprechende Anmerkung den Metadaten des Endpunkts hinzufügen.

Wenn es keine expliziten Anmerkungen gibt, die mittels einer der oben genannten Strategien bereitgestellt werden, versucht das Framework, einen Standardantworttyp zu bestimmen, indem es die Signatur der Antwort untersucht. Diese Standardantwort wird unter dem Statuscode 200 in der OpenAPI-Definition eingetragen.

Mehrere Antworttypen

Wenn ein Endpunkt verschiedene Antworttypen in verschiedenen Szenarien zurückgeben kann, können Sie Metadaten auf folgende Weise bereitstellen:

  • Rufen Sie die Produces-Erweiterungsmethode mehrmals auf, wie im folgenden Beispiel gezeigt:

    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);
    
  • Verwenden Sie Results<TResult1,TResult2,TResultN> in der Signatur und TypedResults im Textkörper des Handlers, wie im folgenden Beispiel gezeigt:

    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();
    });
    

    Die Results<TResult1,TResult2,TResultN>Union-Typen deklarieren, dass ein Routenhandler mehrere IResult implementierende konkrete Typen zurückgibt, und jeder dieser Typen, die IEndpointMetadataProvider implementieren, trägt zu den Metadaten des Endpunkts bei.

    Die Union-Typen implementieren implizite Umwandlungsoperatoren. Diese Operatoren ermöglichen dem Compiler, die in den generischen Argumenten angegebenen Typen automatisch in eine Instanz des Union-Typs zu konvertieren. Diese Fähigkeit hat den zusätzlichen Vorteil, dass zur Kompilierungszeit überprüft wird, ob ein Routenhandler nur die Ergebnisse zurückgibt, die er deklariert. Der Versuch, einen Typ zurückzugeben, der nicht als eines der generischen Argumente für Results<TResult1,TResult2,TResultN> deklariert wurde, führt zu einem Kompilierungsfehler.

Beschreiben von Anforderungstext und -parametern

Neben der Beschreibung der Typen, die von einem Endpunkt zurückgegeben werden, unterstützt OpenAPI auch Anmerkungen zu Eingaben, die von einer API genutzt werden. Diese Eingaben lassen sich in zwei Kategorien einteilen:

  • Parameter, die im Pfad, in der Abfragezeichenfolge, in den Headern oder cookies enthalten sind
  • Daten, die als Teil des Anforderungstexts übermittelt werden

Das Framework leitet die Typen der Anforderungsparameter im Pfad, in der Abfrage und in der Headerzeichenfolge automatisch aus der Signatur des Routenhandlers ab.

Um den Typ der Eingaben zu definieren, die als Anforderungstext übermittelt werden, konfigurieren Sie die Eigenschaften mit der Accepts-Methode, um den Objekt- und Inhaltstyp zu definieren, die vom Anforderungshandler erwartet werden. Im nachstehenden Beispiel akzeptiert der Endpunkt ein Todo-Objekt im Anforderungstext mit dem erwarteten Inhaltstyp application/xml.

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

Zusätzlich zur Erweiterungsmethode Accepts kann ein Parametertyp durch Implementierung der IEndpointParameterMetadataProvider-Schnittstelle seine eigene Anmerkung beschreiben. Beispielsweise fügt der folgende Todo-Typ eine Anmerkung hinzu, die einen Anforderungstext mit dem Inhaltstyp application/xml erfordert.

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

Falls keine explizite Anmerkung angegeben wird, versucht das Framework, den Standardanforderungstyp zu bestimmen, wenn es einen Anforderungstextparameter im Endpunkthandler gibt. Der Rückschluss verwendet die folgende Heuristik, um die Anmerkung zu generieren:

  • Anforderungstextparameter, die über das Attribut [FromForm] aus einem Formular gelesen werden, werden mit dem Inhaltstyp multipart/form-data beschrieben.
  • Alle anderen Anforderungstextparameter werden mit dem Inhaltstyp application/json beschrieben.
  • Der Anforderungstext wird als optional behandelt, wenn er Nullwerte zulässt oder die AllowEmpty-Eigenschaft für das FromBody-Attribut festgelegt ist.

Unterstützung der API-Versionsverwaltung

Minimal-APIs unterstützen über das Paket Asp.Versioning.Http die API-Versionsverwaltung. Beispiele für die Konfiguration der Versionsverwaltung mit Minimal-APIs finden Sie im Repository der API-Versionsverwaltung.

ASP.NET Core OpenAPI-Quellcode auf GitHub

Weitere Ressourcen

Die OpenAPI-Spezifikation ist ein gegenüber der Programmiersprache agnostischer Standard zum Dokumentieren von HTTP-APIs. Dieser Standard wird in Minimal-APIs über eine Kombination aus integrierten APIs und Open-Source-Bibliotheken unterstützt. Es gibt drei wichtige Aspekte für die OpenAPI-Integration in eine Anwendung:

  • Generieren von Informationen zu den Endpunkten in der App
  • Sammeln der Informationen in einem Format, das dem OpenAPI-Schema entspricht
  • Verfügbarmachen des generierten OpenAPI-Schemas über eine visuelle Benutzeroberfläche oder serialisierte Datei

Minimal-APIs bieten über das Paket Microsoft.AspNetCore.OpenApi integrierte Unterstützung für das Generieren von Informationen zu Endpunkten in einer App. Für das Verfügbarmachen der generierten OpenAPI-Definition über eine visuelle Benutzeroberfläche ist ein Paket eines Drittanbieters erforderlich.

Der folgende Code ist aus der minimalen Web-API-Vorlage von ASP.NET Core generiert und verwendet 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);
}

Im hervorgehobenen Code oben:

  • Microsoft.AspNetCore.OpenApi wird im nächsten Abschnitt erläutert.
  • AddEndpointsApiExplorer : Konfiguriert die App so, dass sie den API-Explorer verwendet, um Endpunkte mit Standardanmerkungen zu ermitteln und zu beschreiben. WithOpenApi setzt die Übereinstimmung von Standardanmerkungen, die vom API-Explorer generiert werden, mit denen, die aus dem Microsoft.AspNetCore.OpenApi Paket erstellt wurden, außer Kraft.
  • UseSwaggerfügt die Swagger Middleware hinzu.
  • „UseSwaggerUI“ ermöglicht eine eingebettete Version des Swagger UI-Tools.
  • WithName: Der IEndpointNameMetadata auf dem Endpunkt wird für die Linkgenerierung verwendet und wird als Vorgangs-ID in der OpenAPI-Spezifikation des angegebenen Endpunkts behandelt.
  • WithOpenApi wird später im Artikel erläutert.

Microsoft.AspNetCore.OpenApi NuGet-Paket

ASP.NET Core stellt das Microsoft.AspNetCore.OpenApi Paket bereit, um mit OpenAPI-Spezifikationen für Endpunkte zu interagieren. Das Paket verhält sich wie eine Verknüpfung zwischen den OpenAPI-Modellen, die im Microsoft.AspNetCore.OpenApi Paket definiert sind, und den Endpunkten, die in minimalen APIs definiert sind. Das Paket stellt eine API bereit, welche die Parameter, Antworten und Metadaten eines Endpunkts untersucht, um einen OpenAPI-Anmerkungstyp zu erstellen, der zum Beschreiben eines Endpunkts verwendet wird.

Microsoft.AspNetCore.OpenApi wird als PackageReference zu einer Projektdatei hinzugefügt:

<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>

Wenn Swashbuckle.AspNetCore mit Microsoft.AspNetCore.OpenApi verwendet wird, muss Swashbuckle.AspNetCore 6.4.0 oder höher verwendet werden. Microsoft.OpenApi 1.4.3 oder höher ist erforderlich, um Kopierkonstruktoren in WithOpenApi-Aufrufen zu verwenden.

Hinzufügen von OpenAPI-Anmerkungen zu Endpunkten über WithOpenApi

Durch Aufrufen von WithOpenApi für den Endpunkt werden die Metadaten des Endpunkts ergänzt. Diese Metadaten können:

  • In Drittanbieterpaketen wie Swashbuckle.AspNetCore verarbeitet werden.
  • Auf der Benutzeroberfläche von Swagger oder in YAML oder JSON angezeigt werden, die zum Definieren der API erstellt wurden.
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();

Ändern der OpenAPI-Anmerkung in WithOpenApi

Die WithOpenApi Methode akzeptiert eine Funktion, die benutzt werden kann, um die OpenAPI-Anmerkung zu verändern. Im folgenden Code wird beispielsweise eine Beschreibung zum ersten Parameter des Endpunkts hinzugefügt:

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;
});

Hinzufügen von Vorgangs-IDs zu OpenAPI

Vorgangs-IDs werden verwendet, um einen bestimmten Endpunkt in OpenAPI eindeutig zu identifizieren. Mit der Erweiterungsmethode WithName können Sie die für eine Methode verwendete Vorgangs-ID festlegen.

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

Alternativ kann die OperationId-Eigenschaft auch direkt für die OpenAPI-Anmerkung festgelegt werden.

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

Hinzufügen von Tags zur OpenAPI-Beschreibung

OpenAPI unterstützt Tagobjekte zum Kategorisieren von Vorgängen. Diese Tags werden in der Regel zum Gruppieren von Vorgängen auf der Swagger-Benutzeroberfläche verwendet. Diese Tags können einem Vorgang hinzugefügt werden, indem Sie die Erweiterungsmethode WithTags für den Endpunkt mit den gewünschten Tags aufrufen.

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

Alternativ kann die Liste der OpenApiTags über die OpenAPI-Anmerkung mittels der Erweiterungsmethode WithOpenApi festgelegt werden.

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

Hinzufügen einer Endpunktzusammenfassung oder -beschreibung

Die Endpunktzusammenfassung und -beschreibung kann durch Aufrufen der WithOpenApi-Erweiterungsmethode hinzugefügt werden. Im folgenden Code werden die Zusammenfassungen direkt in der OpenAPI-Anmerkung festgelegt.

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

Ausschließen der OpenAPI-Beschreibung

Im folgenden Beispiel wird der /skipme-Endpunkt von der Generierung einer OpenAPI-Beschreibung ausgeschlossen:

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();

Markieren einer API als veraltet

Wenn Sie einen Endpunkt als veraltet markieren möchten, legen Sie für die OpenAPI-Anmerkung die Eigenschaft Deprecated fest.

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

Beschreiben von Antworttypen

OpenAPI unterstützt die Bereitstellung einer Beschreibung der von einer API zurückgegebenen Antworten. Minimal-APIs unterstützen drei Strategien zum Festlegen des Antworttyps eines Endpunkts:

Mit der Erweiterungsmethode Produces können einem Endpunkt Produces-Metadaten hinzugefügt werden. Wenn keine Parameter angegeben werden, füllt die Erweiterungsmethode die Metadaten für den Zieltyp mit dem Statuscode 200 und dem Inhaltstyp application/json auf.

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

Die Verwendung von TypedResults in der Implementierung des Routenhandlers eines Endpunkts schließt automatisch die Metadaten des Antworttyps für den Endpunkt ein. Der nachstehende Code versieht beispielsweise den Endpunkt automatisch mit einer Antwort unter dem Statuscode 200 mit dem Inhaltstyp application/json.

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

Festlegen von Antworten für ProblemDetails

Wenn Sie den Antworttyp für Endpunkte festlegen, die möglicherweise eine Antwort des Typs ProblemDetails zurückgeben, können Sie mit der Erweiterungsmethode ProducesProblem oder TypedResults.Problem die entsprechende Anmerkung den Metadaten des Endpunkts hinzufügen.

Wenn es keine expliziten Anmerkungen gibt, die mittels einer der oben genannten Strategien bereitgestellt werden, versucht das Framework, einen Standardantworttyp zu bestimmen, indem es die Signatur der Antwort untersucht. Diese Standardantwort wird unter dem Statuscode 200 in der OpenAPI-Definition eingetragen.

Mehrere Antworttypen

Wenn ein Endpunkt verschiedene Antworttypen in verschiedenen Szenarien zurückgeben kann, können Sie Metadaten auf folgende Weise bereitstellen:

  • Rufen Sie die Produces-Erweiterungsmethode mehrmals auf, wie im folgenden Beispiel gezeigt:

    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);
    
  • Verwenden Sie Results<TResult1,TResult2,TResultN> in der Signatur und TypedResults im Textkörper des Handlers, wie im folgenden Beispiel gezeigt:

    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();
    });
    

    Die Results<TResult1,TResult2,TResultN>Union-Typen deklarieren, dass ein Routenhandler mehrere IResult implementierende konkrete Typen zurückgibt, und jeder dieser Typen, die IEndpointMetadataProvider implementieren, trägt zu den Metadaten des Endpunkts bei.

    Die Union-Typen implementieren implizite Umwandlungsoperatoren. Diese Operatoren ermöglichen dem Compiler, die in den generischen Argumenten angegebenen Typen automatisch in eine Instanz des Union-Typs zu konvertieren. Diese Fähigkeit hat den zusätzlichen Vorteil, dass zur Kompilierungszeit überprüft wird, ob ein Routenhandler nur die Ergebnisse zurückgibt, die er deklariert. Der Versuch, einen Typ zurückzugeben, der nicht als eines der generischen Argumente für Results<TResult1,TResult2,TResultN> deklariert wurde, führt zu einem Kompilierungsfehler.

Beschreiben von Anforderungstext und -parametern

Neben der Beschreibung der Typen, die von einem Endpunkt zurückgegeben werden, unterstützt OpenAPI auch Anmerkungen zu Eingaben, die von einer API genutzt werden. Diese Eingaben lassen sich in zwei Kategorien einteilen:

  • Parameter, die im Pfad, in der Abfragezeichenfolge, in den Headern oder cookies enthalten sind
  • Daten, die als Teil des Anforderungstexts übermittelt werden

Das Framework leitet die Typen der Anforderungsparameter im Pfad, in der Abfrage und in der Headerzeichenfolge automatisch aus der Signatur des Routenhandlers ab.

Um den Typ der Eingaben zu definieren, die als Anforderungstext übermittelt werden, konfigurieren Sie die Eigenschaften mit der Accepts-Methode, um den Objekt- und Inhaltstyp zu definieren, die vom Anforderungshandler erwartet werden. Im nachstehenden Beispiel akzeptiert der Endpunkt ein Todo-Objekt im Anforderungstext mit dem erwarteten Inhaltstyp application/xml.

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

Zusätzlich zur Erweiterungsmethode Accepts kann ein Parametertyp durch Implementierung der IEndpointParameterMetadataProvider-Schnittstelle seine eigene Anmerkung beschreiben. Beispielsweise fügt der folgende Todo-Typ eine Anmerkung hinzu, die einen Anforderungstext mit dem Inhaltstyp application/xml erfordert.

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

Falls keine explizite Anmerkung angegeben wird, versucht das Framework, den Standardanforderungstyp zu bestimmen, wenn es einen Anforderungstextparameter im Endpunkthandler gibt. Der Rückschluss verwendet die folgende Heuristik, um die Anmerkung zu generieren:

  • Anforderungstextparameter, die über das Attribut [FromForm] aus einem Formular gelesen werden, werden mit dem Inhaltstyp multipart/form-data beschrieben.
  • Alle anderen Anforderungstextparameter werden mit dem Inhaltstyp application/json beschrieben.
  • Der Anforderungstext wird als optional behandelt, wenn er Nullwerte zulässt oder die AllowEmpty-Eigenschaft für das FromBody-Attribut festgelegt ist.

Unterstützung der API-Versionsverwaltung

Minimal-APIs unterstützen über das Paket Asp.Versioning.Http die API-Versionsverwaltung. Beispiele für die Konfiguration der Versionsverwaltung mit Minimal-APIs finden Sie im Repository der API-Versionsverwaltung.

ASP.NET Core OpenAPI-Quellcode auf GitHub

Weitere Ressourcen

Eine App kann die OpenAPI-Spezifikation für Routenhandler unter Verwendung von Swashbuckle beschreiben.

Der folgende Code ist eine typische ASP.NET Core-App mit OpenAPI-Unterstützung:

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();

Ausschließen der OpenAPI-Beschreibung

Im folgenden Beispiel wird der /skipme-Endpunkt von der Generierung einer OpenAPI-Beschreibung ausgeschlossen:

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();

Beschreiben von Antworttypen

Im folgenden Beispiel werden die integrierten Ergebnistypen verwendet, um die Antwort anzupassen:

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);

Hinzufügen von Vorgangs-IDs zur OpenAPI

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

Hinzufügen von Tags zur OpenAPI-Beschreibung

Im folgenden Code wird ein OpenAPI-Gruppierungstag verwendet:

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