OpenAPI 3.2 in ASP.NET Core

OpenAPI 3.2 in ASP.NET Core

- Matt

Angefangen hat es mit einer Kleinigkeit: Ein Kollege im Ausland wollte für einen Endpunkt, der einen Server-Sent-Events-Strom liefert, ein sauberes Schema im generierten Dokument sehen. In OpenAPI 3.1 gab es dafür keine ehrliche Beschreibung, text/event-stream blieb ein schwarzer Kasten. Genau solche Lücken schließt OpenAPI 3.2, und in ASP.NET Core kommt die Version über die .NET-11-Vorschau ins Haus.

Woher die Version kommt

Die Dokumenterzeugung steckt seit .NET 9 im Paket Microsoft.AspNetCore.OpenApi, eingeschaltet über AddOpenApi() und MapOpenApi(). Neu ist nur, welche Spezifikationsversion dabei herauskommt. In .NET 10 ist das standardmäßig 3.1, ab .NET 11 wird 3.2 der Standard. OpenAPI 3.2 selbst braucht also .NET 11; auf .NET 10 kommt man nicht heran, egal wie man konfiguriert.

Festlegen lässt sich die Version explizit:

builder.Services.AddOpenApi(options =>
{
    options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_2;
});

Wer das Dokument beim Bauen erzeugt statt zur Laufzeit, setzt es über MSBuild:

<OpenApiGenerateDocumentsOptions>--openapi-version OpenApi3_2</OpenApiGenerateDocumentsOptions>

Was 3.2 mitbringt

Drei Dinge fallen im Alltag auf. Erstens die Streaming-Medientypen: Über das neue Feld itemSchema am Media-Type-Objekt lässt sich das Schema eines einzelnen Elements im Strom beschreiben, getrennt vom schema der Gesamtantwort. Damit werden text/event-stream und application/jsonl endlich beschreibbar. Zweitens die Tag-Struktur, die jetzt parent und kind kennt, sodass sich eine Hierarchie und eine Klassifizierung aufbauen lässt statt einer flachen Liste. Drittens die HTTP-Methode QUERY, die eine idempotente Abfrage mit Nutzlast beschreibt, ohne sie als POST tarnen zu müssen.

Der Haken

Die Erzeugung ist die eine Hälfte, das Lesen die andere. Das generierte Dokument mag valides 3.2 sein, nur muss die Gegenstelle es auch verstehen. Ältere Swagger-UI-Stände und diverse Codegeneratoren kennen 3.2 schlicht noch nicht und stolpern über unbekannte Felder oder brechen ab. Bei mir hat ein Client-Generator die Datei kommentarlos verworfen, bis ich die Version wieder auf 3.1 gestellt habe. Solange die Werkzeugkette der Konsumenten nicht nachgezogen ist, bleibt 3.1 die sicherere Ausgabe, und man behält die 3.2-spezifischen Felder für den Tag im Hinterkopf, an dem alle Seiten mitziehen. Wie sich diese Erzeugung an eigene Endpunkte anfügt, hatte ich bei den Minimal APIs nach zwei Jahren beschrieben.