OpenAPI-Dokumente generieren
Das Paket Microsoft.AspNetCore.OpenApi
bietet integrierte Unterstützung für die OpenAPI-Dokumentgenerierung in ASP.NET Core. Das Paket bietet folgende Funktionen:
- Unterstützung für das Generieren von OpenAPI-Dokumenten zur Laufzeit und Zugriff darauf über einen Endpunkt in der Anwendung
- Unterstützung für „Transformator“-APIs, die das Ändern des generierten Dokuments ermöglichen
- Unterstützung für das Generieren mehrerer OpenAPI-Dokumente aus einer einzelnen App
- Nutzt die Unterstützung des JSON-Schemas, das von
System.Text.Json
bereitgestellt wird. - ist mit nativem AOT kompatibel.
Paketinstallation
Installieren Sie das Paket Microsoft.AspNetCore.OpenApi
:
Führen Sie den folgenden Befehl in der Paket-Manager-Konsole aus:
Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease
Um Unterstützung für das Generieren von OpenAPI-Dokumenten zum Zeitpunkt der Erstellung hinzuzufügen, installieren Sie das Microsoft.Extensions.ApiDescription.Server
-Paket:
Führen Sie den folgenden Befehl in der Paket-Manager-Konsole aus:
Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease
Konfigurieren der OpenAPI-Dokumentgenerierung
Der folgende Code führt folgende Aktionen aus:
- Fügt OpenAPI-Dienste hinzu.
- Aktiviert den Endpunkt für die Anzeige des OpenAPI-Dokuments im JSON-Format.
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/", () => "Hello world!");
app.Run();
Starten Sie die App, und navigieren Sie zu https://localhost:<port>/openapi/v1.json
, um das generierte OpenAPI-Dokument anzuzeigen.
Einschließen von OpenAPI-Metadaten in eine ASP.NET-Web-App
Einschließen von OpenAPI-Metadaten für Endpunkte
ASP.NET sammelt Metadaten von den Endpunkten der Web-App und verwendet sie zum Generieren eines OpenAPI-Dokuments.
In controllerbasierten Apps werden Metadaten aus Attributen wie [EndpointDescription]
, [HttpPost]
und [Produces]
gesammelt.
In minimalen APIs können Metadaten aus Attributen gesammelt werden, sie können aber auch mithilfe von Erweiterungsmethoden und anderen Strategien festgelegt werden, z. B. das Zurückgeben von TypedResults von Routenhandlern.
Die folgende Tabelle enthält eine Übersicht über die gesammelten Metadaten und die Strategien zum Festlegen der Metadaten.
Metadaten | Attribut | Erweiterungsmethode | Weitere Strategien |
---|---|---|---|
Zusammenfassung | [EndpointSummary] |
WithSummary | |
Beschreibung | [EndpointDescription] |
WithDescription | |
Tags | [Tags] |
WithTags | |
operationId | [EndpointName] |
WithName | |
Parameter | [FromQuery] , , [FromRoute] [FromHeader] [FromForm] |
||
Parameterbeschreibung | [EndpointDescription] |
||
requestBody | [FromBody] |
Accepts | |
Antworten | [Produces] |
Produces, ProducesProblem | TypedResults |
Ausschließen von Endpunkten | [ExcludeFromDescription] , [ApiExplorerSettings] |
ExcludeFromDescription |
ASP.NET Core sammelt keine Metadaten aus XML-Dokumentkommentaren.
In den folgenden Abschnitten wird veranschaulicht, wie Sie Metadaten in eine App integrieren, um das generierte OpenAPI-Dokument anzupassen.
Zusammenfassung und Beschreibung
Die Endpunktzusammenfassung und -beschreibung kann mit den [EndpointSummary]
- und [EndpointDescription]
-Attributen oder in minimalen APIs mit den WithSummary- und WithDescription-Erweiterungsmethoden festgelegt werden.
Im folgenden Beispiel werden die verschiedenen Strategien zum Festlegen von Zusammenfassungen und Beschreibungen veranschaulicht.
Beachten Sie, dass die Attribute in der Delegatmethode und nicht in der app.MapGet-Methode platziert werden.
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 unterstützt die Angabe von Tags auf jedem Endpunkt als Form der Kategorisierung.
In minimalen APIs können Tags entweder mithilfe des [Tags]
-Attributs oder der WithTags-Erweiterungsmethode festgelegt werden.
Im folgenden Beispiel werden die verschiedenen Strategien zum Festlegen von Tags veranschaulicht.
app.MapGet("/extension-methods", () => "Hello world!")
.WithTags("todos", "projects");
app.MapGet("/attributes",
[Tags("todos", "projects")]
() => "Hello world!");
operationId
OpenAPI unterstützt eine operationId für jeden Endpunkt als eindeutigen Bezeichner oder Namen für den Vorgang.
In minimalen APIs kann die operationId entweder mithilfe des [EndpointName]
-Attributs oder der WithName-Erweiterungsmethode festgelegt werden.
Im folgenden Beispiel werden die verschiedenen Strategien zum Festlegen der operationId veranschaulicht.
app.MapGet("/extension-methods", () => "Hello world!")
.WithName("FromExtensionMethods");
app.MapGet("/attributes",
[EndpointName("FromAttributes")]
() => "Hello world!");
Parameter
OpenAPI unterstützt Anmerkungen zu Pfad, Abfragezeichenfolge, Header und cookie-Parametern, die von einer API genutzt werden.
Das Framework leitet die Typen der Anforderungsparameter automatisch aus der Signatur des Routenhandlers ab.
Das [EndpointDescription]
-Attribut kann verwendet werden, um eine Beschreibung für einen Parameter bereitzustellen.
Im folgenden Beispiel wird veranschaulicht, wie eine Beschreibung für einen Parameter festgelegt wird.
app.MapGet("/attributes",
([Description("This is a description.")] string name) => "Hello world!");
Beschreiben des Anforderungstexts
Das requestBody
Feld in OpenAPI beschreibt den Textkörper einer Anforderung, die ein API-Client an den Server senden kann, einschließlich der unterstützten Inhaltstypen und des Schemas für den Textinhalt.
Wenn die Endpunkthandlermethode Parameter akzeptiert, die vom Anforderungstext gebunden sind, generiert ASP.NET Core einen entsprechenden requestBody
Vorgang im OpenAPI-Dokument. Metadaten für den Anforderungstext können auch mithilfe von Attributen oder Erweiterungsmethoden angegeben werden. Zusätzliche Metadaten können mit einem Dokumenttransformor oder Betriebstransformator festgelegt werden.
Wenn der Endpunkt keine Parameter definiert, die an den Anforderungstext gebunden sind, sondern stattdessen den Anforderungstext direkt HttpContext verwendet, stellt ASP.NET Core Mechanismen zum Angeben von Anforderungstextmetadaten bereit. Dies ist ein häufiges Szenario für Endpunkte, die den Anforderungstext als Datenstrom verarbeiten.
Einige Anforderungstextmetadaten können anhand der FromBody
Parameter der FromForm
Route-Handlermethode bestimmt werden.
Eine Beschreibung für den Anforderungstext kann mit einem [Description]
Attribut für den Parameter mit FromBody
oder .FromForm
Wenn der FromBody
Parameter nicht nullfähig ist und EmptyBodyBehavior nicht im FromBody
Attribut festgelegt Allow ist, ist der Anforderungstext erforderlich, und das required
Feld des requestBody
Parameters wird im generierten OpenAPI-Dokument festgelegttrue
.
Formularkörper sind immer erforderlich und müssen required
auf true
.
Verwenden Sie einen Dokumenttransformor oder einen Vorgangstransformor, um die example
Spezifikationenerweiterungen examples
für den Anforderungstext im generierten OpenAPI-Dokument festzulegen, oder encoding
um Spezifikationserweiterungen für den Anforderungstext hinzuzufügen.
Andere Mechanismen zum Festlegen von Anforderungstextmetadaten hängen vom Typ der zu entwickelnden App ab und werden in den folgenden Abschnitten beschrieben.
Die Inhaltstypen für den Anforderungstext im generierten OpenAPI-Dokument werden vom Typ des Parameters bestimmt, der an den Anforderungstext gebunden oder mit der Accepts Erweiterungsmethode angegeben ist.
Standardmäßig lautet der Inhaltstyp eines FromBody
Parameters und der Inhaltstyp für FromForm
Parameter multipart/form-data
oder application/x-www-form-urlencoded
.application/json
Die Unterstützung für diese Standardinhaltstypen ist in minimale APIs integriert, und andere Inhaltstypen können mithilfe einer benutzerdefinierten Bindung behandelt werden. Weitere Informationen finden Sie im Thema "Benutzerdefinierte Bindung " der Dokumentation zu minimalen APIs.
Es gibt mehrere Möglichkeiten, einen anderen Inhaltstyp für den Anforderungstext anzugeben.
Wenn der Typ des FromBody
Parameters implementiert IEndpointParameterMetadataProviderwird, verwendet ASP.NET Core diese Schnittstelle, um die Inhaltstypen im Anforderungstext zu bestimmen.
Das Framework verwendet die PopulateMetadata Methode dieser Schnittstelle, um die Inhaltstypen und den Typ des Textkörperinhalts des Anforderungstexts festzulegen. Beispielsweise kann IEndpointParameterMetadataProvider eine Todo
Klasse, die eine application/xml
oder text/xml
einen Inhaltstyp akzeptiert, verwenden, um diese Informationen für das Framework bereitzustellen.
public class Todo : IEndpointParameterMetadataProvider
{
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
builder.Metadata.Add(new AcceptsMetadata(["application/xml", "text/xml"], typeof(Todo)));
}
}
Die Accepts Erweiterungsmethode kann auch verwendet werden, um den Inhaltstyp des Anforderungstexts anzugeben.
Im nachstehenden Beispiel akzeptiert der Endpunkt ein Todo
-Objekt im Anforderungstext mit dem erwarteten Inhaltstyp application/xml
.
app.MapPut("/todos/{id}", (int id, Todo todo) => ...)
.Accepts<Todo>("application/xml");
Da application/xml
es sich nicht um einen integrierten Inhaltstyp handelt, muss die Klasse die Todo
IBindableFromHttpContext<TSelf> Schnittstelle implementieren, um eine benutzerdefinierte Bindung für den Anforderungstext bereitzustellen. Zum Beispiel:
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());
}
Wenn der Endpunkt keine Parameter definiert, die an den Anforderungstext gebunden sind, verwenden Sie die Accepts Erweiterungsmethode, um den Vom Endpunkt akzeptierten Inhaltstyp anzugeben.
Wenn Sie AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepts%2A> mehrmals angeben<, werden nur Metadaten aus dem letzten verwendet – sie werden nicht kombiniert.
Beschreiben von Antworttypen
OpenAPI unterstützt die Bereitstellung einer Beschreibung der von einer API zurückgegebenen Antworten. ASP.NET Core bietet mehrere Strategien zum Festlegen der Antwortmetadaten eines Endpunkts. Antwortmetadaten, die festgelegt werden können, umfassen den Statuscode, den Typ des Antworttexts und Inhaltstypen einer Antwort. Antworten in OpenAPI können zusätzliche Metadaten enthalten, z. B. Beschreibung, Header, Links und Beispiele. Diese zusätzlichen Metadaten können mit einem Dokumenttransformor oder Betriebstransformator festgelegt werden.
Die spezifischen Mechanismen zum Festlegen von Antwortmetadaten hängen vom Typ der zu entwickelnden App ab.
In minimalen API-Apps kann ASP.NET Core die Antwortmetadaten extrahieren, die von Erweiterungsmethoden auf dem Endpunkt hinzugefügt werden, Attribute im Routenhandler und den Rückgabetyp des Routenhandlers.
- Die Produces Erweiterungsmethode kann auf dem Endpunkt verwendet werden, um den Statuscode, den Typ des Antworttexts und Inhaltstypen einer Antwort von einem Endpunkt anzugeben.
- Das Attribut oder ProducesResponseTypeAttribute<T> das
[ProducesResponseType]
Attribut kann verwendet werden, um den Typ des Antworttexts anzugeben. - Ein Routenhandler kann verwendet werden, um einen Typ zurückzugeben, der implementiert, IEndpointMetadataProvider um den Typ und die Inhaltstypen des Antworttexts anzugeben.
- Die ProducesProblem Erweiterungsmethode auf dem Endpunkt kann verwendet werden, um den Statuscode und die Inhaltstypen einer Fehlerantwort anzugeben.
Beachten Sie, dass die Produces Methoden und ProducesProblem Erweiterungsmethoden sowohl für als auch RouteHandlerBuilder für "On RouteGroupBuilder" unterstützt werden. Dies ermöglicht z. B. eine gemeinsame Gruppe von Fehlerantworten, die für alle Vorgänge in einer Gruppe definiert werden.
Wenn sie nicht durch eine der vorherigen Strategien angegeben wird, lautet die:
- Statuscode für die Antwortstandardwerte auf 200.
- Das Schema für den Antworttext kann vom impliziten oder expliziten Rückgabetyp der Endpunktmethode abgeleitet werden, z. B. von
T
in Task<TResult>; andernfalls gilt es als nicht angegeben. - Der Inhaltstyp für den angegebenen oder abgeleiteten Antworttext lautet "application/json".
In minimalen APIs legen die Produces Erweiterungsmethode und das [ProducesResponseType]
Attribut nur die Antwortmetadaten für den Endpunkt fest. Sie ändern oder beschränken nicht das Verhalten des Endpunkts, der möglicherweise einen anderen Statuscode oder Antworttexttyp zurückgibt als durch die Metadaten angegeben, und der Inhaltstyp wird durch den Rückgabetyp der Route-Handlermethode bestimmt, unabhängig von jedem in Attributen oder Erweiterungsmethoden angegebenen Inhaltstyp.
Die Produces Erweiterungsmethode kann den Antworttyp eines Endpunkts mit einem Standardstatuscode von 200 und einem Standardinhaltstyp angeben application/json
. Das folgende Beispiel veranschaulicht dies:
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.Produces<IList<Todo>>();
Dies [ProducesResponseType]
kann verwendet werden, um einem Endpunkt Antwortmetadaten hinzuzufügen. Beachten Sie, dass das Attribut auf die Routehandlermethode angewendet wird, nicht auf den Methodenaufruf zum Erstellen der Route, wie im folgenden Beispiel gezeigt:
app.MapGet("/todos",
[ProducesResponseType<List<Todo>>(200)]
async (TodoDb db) => await db.Todos.ToListAsync());
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);
});
Nur Rückgabetypen, die einen responses
Eintrag im OpenAPI-Dokument implementierenIEndpointMetadataProvider. Im Folgenden finden Sie eine Teilliste einiger Hilfsmethoden, die TypedResults einen responses
Eintrag erzeugen:
TypedResults-Hilfsmethode | status code |
---|---|
Ok() | 200 |
Created() | 201 |
CreatedAtRoute() | 201 |
Accepted() | 202 |
AcceptedAtRoute() | 202 |
NoContent() | 204 |
BadRequest() | 400 |
ValidationProblem() | 400 |
NotFound() | 404 |
Conflict() | 409 |
UnprocessableEntity() | 422 |
Alle diese Methoden mit Ausnahme NoContent
einer generischen Überladung, die den Typ des Antworttexts angibt.
Eine Klasse kann implementiert werden, um die Endpunktmetadaten festzulegen und aus dem Routenhandler zurückzugeben.
Festlegen von Antworten für ProblemDetails
Wenn Sie den Antworttyp für Endpunkte festlegen, die eine ProblemDetails-Antwort zurückgeben können, kann folgendes verwendet werden, um die entsprechenden Antwortmetadaten für den Endpunkt hinzuzufügen:
- ProducesProblem
- Die Erweiterungsmethode ProducesValidationProblem
- TypedResults mit einem Statuscode im Bereich (400-499).
Weitere Informationen zum Konfigurieren einer minimalen API-App zum Zurückgeben von ProblemDetails-Antworten finden Sie unter Behandeln von Fehlern in minimalen APIs.
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,TResult3,TResult4,TResult5,TResult6> 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>
-Uniontypen deklarieren, dass ein Routenhandler mehrereIResult
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.
Ausschließen von Endpunkten aus dem generierten Dokument
Standardmäßig werden alle in einer App definierten Endpunkte in der generierten OpenAPI-Datei dokumentiert, Endpunkte können jedoch mithilfe von Attributen oder Erweiterungsmethoden vom Dokument ausgeschlossen werden.
Der Mechanismus zum Angeben eines Endpunkts, der ausgeschlossen werden soll, hängt vom Typ der zu entwickelnden App ab.
Minimale APIs unterstützen zwei Strategien zum Ausschließen eines bestimmten Endpunkts aus dem OpenAPI-Dokument:
- ExcludeFromDescription-Erweiterungsmethode
[ExcludeFromDescription]
-Attribut
Im folgenden Beispiel werden die verschiedenen Strategien zum Ausschließen eines bestimmten Endpunkts aus dem generierten OpenAPI-Dokument veranschaulicht.
app.MapGet("/extension-method", () => "Hello world!")
.ExcludeFromDescription();
app.MapGet("/attributes",
[ExcludeFromDescription]
() => "Hello world!");
Einschließen von OpenAPI-Metadaten für Datentypen
C#-Klassen oder Datensätze, die in Anforderungs- oder Antworttexten verwendet werden, werden im generierten OpenAPI-Dokument als Schemata dargestellt. Standardmäßig werden nur öffentliche Eigenschaften im Schema dargestellt, aber es gibt auch JsonSerializerOptions, um Schemaeigenschaften für Felder zu erstellen.
Wenn die PropertyNamingPolicy auf gemischte Groß-/Kleinschreibung festgelegt ist (dies ist die Standardeinstellung in ASP.NET-Webanwendungen), sind Eigenschaftsnamen in einem Schema in einem gemischten Groß-/Kleinschreibungsformat des Klassen- oder Datensatzeigenschaftsnamens.
Das [JsonPropertyName]
kann für eine einzelne Eigenschaft verwendet werden, um den Namen der Eigenschaft im Schema anzugeben.
Typ und Format
Die JSON-Schemabibliothek ordnet Standard-C#-Typen dem OpenAPI-type
und -format
wie folgt zu:
C#-Typ | OpenAPI type |
OpenAPI format |
---|---|---|
int | integer | int32 |
long | integer | int64 |
short | integer | int16 |
Byte | integer | uint8 |
float | Zahl | float |
double | Zahl | double |
Decimal | Zahl | double |
bool | boolean | |
Zeichenfolge | Zeichenfolge | |
char | Zeichenfolge | char |
byte[] | Zeichenfolge | Byte |
DateTimeOffset | Zeichenfolge | date-time |
DateOnly | Zeichenfolge | date |
TimeOnly | Zeichenfolge | time |
URI | Zeichenfolge | uri |
Guid | Zeichenfolge | uuid |
Objekt | nicht angegeben | |
dynamisch | nicht angegeben |
Beachten Sie, dass für Objekt- und dynamische Typen kein Typ in der OpenAPI definiert ist, da sie Daten beliebigen Typs enthalten können, einschließlich einfacher Typen wie int oder string.
Die type
und format
können auch mit einem Schema-Transformator eingestellt werden. Sie möchten beispielsweise, dass die format
der Dezimaltypen decimal
statt double
ist.
Verwenden von Attributen zum Hinzufügen von Metadaten
ASP.NET verwendet Metadaten aus Attributen von Klassen- oder Datensatzeigenschaften, um Metadaten für die entsprechenden Eigenschaften des generierten Schemas festzulegen.
Die folgende Tabelle fasst die Attribute aus dem System.ComponentModel
-Namespace zusammen, die Metadaten für das generierte Schema bereitstellen:
Attribute | Beschreibung |
---|---|
[Description] |
Legt die description einer Eigenschaft im Schema fest. |
[Required] |
Markiert eine Eigenschaft wie required im Schema. |
[DefaultValue] |
Legt den default -Wert einer Eigenschaft im Schema fest. |
[Range] |
Legt den minimum -Wert einer maximum Ganzzahl oder Zahl fest. |
[MinLength] |
Legt die minLength einer Zeichenfolge fest. |
[MaxLength] |
Setzt die maxLength einer Zeichenfolge. |
[RegularExpression] |
Legt das pattern einer Zeichenfolge fest. |
Beachten Sie, dass diese Attribute in Controller-basierten Apps dem Vorgang Filter hinzufügen, um zu überprüfen, ob eingehende Daten die Einschränkungen erfüllen. In minimalen APIs legen diese Attribute die Metadaten im generierten Schema fest, aber die Überprüfung muss explizit über einen Endpunktfilter, in der Logik des Routenhandlers oder über ein Drittanbieterpaket ausgeführt werden.
Andere Metadatenquellen für generierte Schemata
erforderlich
Eigenschaften können mit dem Modifikator erforderlich auch als required
markiert werden.
enum
Enumerationstypen in C# sind ganzzahlbasiert, können aber als Zeichenfolgen in JSON mit einem [JsonConverter]
und einem JsonStringEnumConverter dargestellt werden. Wenn ein Enumerationstyp als Zeichenfolge in JSON dargestellt wird, verfügt das generierte Schema über eine Eigenschaft enum
mit den Zeichenfolgenwerten der Enumeration.
Ein Enumerationstyp ohne [JsonConverter]
wird im generierten Schema wie type: integer
definiert.
Hinweis: Das [AllowedValues]
legt die enum
-Werte einer Eigenschaft nicht fest.
nullable
Eigenschaften, die als Nullwerte zulassend oder als Verweistyp definiert sind, haben im generierten Schema nullable: true
. Dies entspricht dem Standardverhalten des System.Text.Json-Deserialisierers, der null
als gültigen Wert für eine Nullable-Eigenschaft akzeptiert.
additionalProperties
Schemata werden standardmäßig ohne Assertion additionalProperties
generiert, was den Standardwert von true
impliziert. Dies entspricht dem Standardverhalten des System.Text.Json-Deserializers, dass zusätzliche Eigenschaften in einem JSON-Objekt stillschweigend ignoriert werden
Wenn die zusätzlichen Eigenschaften eines Schemas nur Werte eines bestimmten Typs aufweisen sollen, definieren Sie die Eigenschaft oder Klasse als eine Dictionary<string, type>
. Der Schlüsseltyp für das Wörterbuch muss string
sein. Dadurch wird ein Schema generiert, in dem additionalProperties
das Schema für „type“ als die erforderlichen Wertetypen angibt.
Metadaten für polymorphe Typen
Verwenden Sie die Attribute [JsonPolymorphic]
und [JsonDerivedType]
einer übergeordneten Klasse, um das Diskriminatorfeld und die Untertypen für einen polymorphen Typ anzugeben.
Die [JsonDerivedType]
fügt dem Schema für jede Unterklasse das Diskriminatorfeld hinzu, wobei eine Enumeration den spezifischen Diskriminatorwert für die Unterklasse angibt. Dieses Attribut ändert auch den Konstruktor jeder abgeleiteten Klasse, um den Diskriminatorwert festzulegen.
Eine abstrakte Klasse mit einem [JsonPolymorphic]
-Attribut weist ein discriminator
-Feld im Schema auf, aber eine konkrete Klasse mit einem [JsonPolymorphic]
-Attribut verfügt nicht über ein discriminator
-Feld. OpenAPI erfordert, dass die Diskriminatoreigenschaft eine erforderliche Eigenschaft im Schema sein muss, aber da die Diskriminatoreigenschaft nicht in der konkreten Basisklasse definiert ist, kann das Schema kein discriminator
-Feld enthalten.
Hinzufügen von Metadaten mit einem Schematransformator
Ein Schematransformator kann verwendet werden, um alle Standardmetadaten zu überschreiben oder dem generierten Schema zusätzliche Metadaten hinzuzufügen, z. B. example
-Werte. Weitere Informationen finden Sie unter Verwenden von Schematransformatoren.
Optionen zum Anpassen der OpenAPI-Dokumentgenerierung
In den folgenden Abschnitten wird veranschaulicht, wie Sie die OpenAPI-Dokumentgenerierung anpassen.
Anpassen des OpenAPI-Dokumentnamens
Jedes OpenAPI-Dokument in einer App hat einen eindeutigen Namen. Der registrierte Standarddokumentname ist v1
.
builder.Services.AddOpenApi(); // Document name is v1
Der Dokumentname kann geändert werden, indem der Name als Parameter an den AddOpenApi
-Aufruf übergeben wird.
builder.Services.AddOpenApi("internal"); // Document name is internal
Der Dokumentname wird an mehreren Stellen in der OpenAPI-Implementierung angezeigt.
Beim Abrufen des generierten OpenAPI-Dokuments wird der Dokumentname als documentName
-Parameterargument in der Anforderung angegeben. Die folgenden Anforderungen lösen die Dokumente v1
und internal
auf.
GET http://localhost:5000/openapi/v1.json
GET http://localhost:5000/openapi/internal.json
Anpassen der OpenAPI-Version eines generierten Dokuments
Standardmäßig generiert die OpenAPI-Dokumentgenerierung ein Dokument, das mit v3.0 der OpenAPI-Spezifikation kompatibel ist. Der folgende Code veranschaulicht, wie die Standardversion des OpenAPI-Dokuments geändert wird:
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0;
});
Anpassen der OpenAPI-Endpunktroute
Standardmäßig macht der über einen Aufruf von MapOpenApi registrierte OpenAPI-Endpunkt das Dokument am /openapi/{documentName}.json
-Endpunkt verfügbar. Der folgende Code veranschaulicht, wie Sie die Route anpassen, mit der das OpenAPI-Dokument registriert ist:
app.MapOpenApi("/openapi/{documentName}/openapi.json");
Der documentName
-Routenparameter kann aus der Endpunktroute entfernt werden, dies wird aber nicht empfohlen. Wenn der Routenparameter documentName
aus der Endpunktroute entfernt wird, versucht das Framework, den Dokumentnamen aus dem Abfrageparameter aufzulösen. Wenn documentName
weder in der Route noch in der Abfrage angegeben wird, kann das zu unerwartetem Verhalten führen.
Anpassen des OpenAPI-Endpunkts
Da das OpenAPI-Dokument über einen Routenhandler-Endpunkt bereitgestellt wird, stehen alle Anpassungen, die für standardmäßige minimale Endpunkte verfügbar sind, für den OpenAPI-Endpunkt zur Verfügung.
Einschränken des Zugriffs auf das OpenAPI-Dokument auf autorisierte Benutzer:innen
Der OpenAPI-Endpunkt aktiviert standardmäßig keine Autorisierungsprüfungen. Autorisierungsprüfungen können jedoch auf das OpenAPI-Dokument angewendet werden. Mit dem folgenden Code wird der Zugriff auf das OpenAPI-Dokument beispielsweise auf Personen mit der Rolle tester
beschränkt:
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();
Zwischenspeichern des generierten OpenAPI-Dokuments
Das OpenAPI-Dokument wird jedes Mal erneut generiert, wenn eine Anforderung an den OpenAPI-Endpunkt gesendet wird. Durch die erneute Generierung können die Transformatoren dynamisch den Anwendungszustand in ihre Vorgänge integrieren. So können Sie z. B. eine Anforderung mit Details des HTTP-Kontexts neu generieren. Dementsprechend kann das OpenAPI-Dokument zwischengespeichert werden, um die Ausführung der Dokumentgenerierungspipeline für jede HTTP-Anforderung zu vermeiden.
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();
Transformatoren für OpenAPI-Dokumente
In diesem Abschnitt wird veranschaulicht, wie OpenAPI-Dokumente mit Transformatoren angepasst werden.
Anpassen von OpenAPI-Dokumenten mit Transformatoren
Transformatoren bieten eine API zum Ändern des OpenAPI-Dokuments mit benutzerdefinierten Anpassungen. Transformatoren eignen sich z. B. für folgende Szenarien:
- Hinzufügen von Parametern zu allen Vorgängen in einem Dokument
- Ändern der Beschreibungen von Parametern oder Vorgängen
- Hinzufügen von Informationen auf oberster Ebene zum OpenAPI-Dokument
Transformatoren sind in drei Kategorien unterteilt:
- Dokumenttransformatoren haben Zugriff auf das gesamte OpenAPI-Dokument. Sie können verwendet werden, um globale Änderungen am Dokument vorzunehmen.
- Vorgangstransformatoren gelten jeweils für einen einzelnen Vorgang. Jeder einzelne Vorgang ist eine Kombination aus Pfad und HTTP-Methode. Sie können verwendet werden, um Parameter oder Antworten auf Endpunkten zu ändern.
- Schematransformatoren gelten für jedes Schema im Dokument. Diese können verwendet werden, um das Schema von Anforderungs- oder Antworttexten oder geschachtelten Schemata zu ändern.
Transformer können über den Aufruf der AddDocumentTransformer-Methode im OpenApiOptions-Objekt im Dokument registriert werden. Der folgende Codeschnipsel zeigt verschiedene Möglichkeiten zum Registrieren von Transformatoren im Dokument:
- Registrieren eines Dokumenttransformators mithilfe eines Delegaten
- Registrieren eines Dokumenttransformators mithilfe einer Instanz von IOpenApiDocumentTransformer
- Registrieren eines Dokumenttransformators mithilfe eines DI-fähigen IOpenApiDocumentTransformer
- Registrieren eines Vorgangstransformators mithilfe eines Delegaten
- Registrieren eines Vorgangstransformators mithilfe einer Instanz von IOpenApiOperationTransformer.
- Registrieren eines Vorgangstransformators mithilfe eines DI-fähigen IOpenApiOperationTransformer.
- Registrieren eines Schematransformators mithilfe eines Delegaten.
- Registrieren eines Schematransformators mithilfe einer Instanz von IOpenApiSchemaTransformer.
- Registrieren eines Schematransformators mithilfe eines DI-fähigen 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();
Ausführungsreihenfolge für Transformer
Transformer werden basierend auf der Registrierung in First-In-First-Out-Reihenfolge ausgeführt. Im folgenden Codeschnipsel hat der Dokumenttransformator Zugriff auf die vom Vorgangstransformator vorgenommenen Änderungen:
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();
Verwenden von Dokumenttransformatoren
Dokumenttransformer haben Zugriff auf ein Kontextobjekt, das Folgendes umfasst:
- den Namen des zu ändernden Dokuments
- Das ApiDescriptionGroups diesem Dokument zugeordnete Dokument.
- Der bei der Dokumentgenerierung verwendete IServiceProvider.
Dokumenttransformatoren können auch das generierte OpenAPI-Dokument ändern. Im folgenden Beispiel wird ein Dokumenttransformator veranschaulicht, der dem OpenAPI-Dokument einige Informationen zur API hinzufügt.
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();
Vom Dienst aktivierte Dokumenttransformatoren können Instanzen der Dokumentinstanz verwenden, um die App zu ändern. Im folgenden Beispiel wird ein Dokumenttransformator gezeigt, der den IAuthenticationSchemeProvider-Dienst auf Authentifizierungsebene verwendet. Er überprüft, ob alle Schemas für JWT-Bearer in der App registriert sind, und fügt sie auf der obersten Ebene des OpenAPI-Dokuments hinzu:
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;
}
}
}
Dokumenttransformatoren sind für die Dokumentinstanz eindeutig, der sie zugeordnet sind. Im folgenden Beispiel führt ein Transformator folgende Aktionen aus:
- Er registriert authentifizierungsbezogene Anforderungen für das Dokument
internal
. - Er lässt das Dokument
public
unverändert.
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>()
});
}
}
}
}
Verwenden von Vorgangstransformatoren
Vorgänge sind eindeutige Kombinationen von HTTP-Pfaden und -Methoden in einem OpenAPI-Dokument. Vorgangstransformatoren sind bei folgenden Änderungen hilfreich:
- Änderungen für jeden Endpunkt einer App oder
- Bedingte Änderungen an bestimmten Routen
Vorgangstransformer haben Zugriff auf ein Kontextobjekt, das Folgendes enthält:
- den Namen des Dokuments, zu dem der Vorgang gehört
- ApiDescription, das dem Vorgang zugeordnet ist.
- Der bei der Dokumentgenerierung verwendete IServiceProvider.
Beispielsweise fügt der folgende Vorgangstransformator 500
als Antwortstatuscode hinzu, der von allen Vorgängen im Dokument unterstützt wird.
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();
Verwenden von Schematransformatoren
Schemas sind die Datenmodelle, die in Anforderungs- und Antworttextkörpern in einem OpenAPI-Dokument verwendet werden. Schematransformatoren sind nützlich, wenn eine Änderung:
- an jedem Schema im Dokument vorgenommen werden sollte oder
- bedingt auf bestimmte Schemata angewendet werden sollte.
Schematransformatoren haben Zugriff auf ein Kontextobjekt, das Folgendes enthält:
- den Namen des Dokuments, zu dem der Vorgang gehört.
- die JSON-Typinformationen, die dem Zielschema zugeordnet sind.
- Der bei der Dokumentgenerierung verwendete IServiceProvider.
Der folgende Schematransformator setzt zum Beispiel das format
von Dezimaltypen auf decimal
statt auf double
:
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; }
}
Zusätzliche Ressourcen
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. Informationen zur Unterstützung von OpenAPI in controllerbasierten APIs finden Sie in der .NET 9-Version dieses Artikels.
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 demMicrosoft.AspNetCore.OpenApi
Paket erstellt wurden, außer Kraft. UseSwagger
fü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.
- Angezeigt in der Benutzeroberfläche von Swagger, oder in YAML oder JSON, das zum Definieren der API erstellt wurde.
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:
- Über die Erweiterungsmethode
Produces
für den Endpunkt - Über das Attribut
ProducesResponseType
für den Routenhandler - Durch Zurückgeben von
TypedResults
aus dem Routenhandler
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
Beim Festlegen des Antworttyps für Endpunkte, die eine ProblemDetails-Antwort zurückgeben können, kann die Erweiterungsmethode ProducesProblem, ProducesValidationProblem oder TypedResults.Problem
verwendet werden, um den entsprechenden Kommentar zu den Metadaten des Endpunkts hinzuzufügen. Beachten Sie, dass die Erweiterungsmethoden ProducesProblem
und ProducesValidationProblem
nicht mit Routengruppen in .NET 8 und früher verwendet werden können.
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 undTypedResults
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>
-Uniontypen deklarieren, dass ein Routenhandler mehrereIResult
implementierende konkrete Typen zurückgibt, und jeder dieser Typen, dieIEndpointMetadataProvider
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 Kopfzeilen oder in Cookies erscheinen
- 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 Inhaltstypmultipart/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 dasFromBody
-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 minimale API-App kann die OpenAPI-Spezifikation für Routenhandler unter Verwendung von Swashbuckle beschreiben.
Informationen zur Unterstützung von OpenAPI in controllerbasierten APIs finden Sie in der .NET 9-Version dieses Artikels.
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");