Freigeben über


Anpassen von OpenAPI-Dokumenten

Transformatoren für OpenAPI-Dokumente

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:

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

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

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

app.Run();

Ausführungsreihenfolge für Transformer

Transformatoren werden in der folgenden Reihenfolge betrieben.

  • Schematransformatoren werden ausgeführt, wenn ein Schema für das Dokument registriert ist. Sie werden in der Reihenfolge ausgeführt, in der sie hinzugefügt werden. Alle Schemas werden dem Dokument hinzugefügt, bevor eine Vorgangsverarbeitung erfolgt, sodass Schematransformatoren vor Betriebstransformatoren ausgeführt werden.
  • Vorgangstransformatoren werden ausgeführt, wenn dem Dokument ein Vorgang hinzugefügt wird. Sie werden in der Reihenfolge ausgeführt, in der sie hinzugefügt werden. Alle Operationen werden dem Dokument hinzugefügt, bevor Dokumenttransformatoren ausgeführt werden.
  • Dokumenttransformatoren werden ausgeführt, wenn das Dokument generiert wird. Dies ist der letzte Durchlauf des Dokuments, und alle Vorgänge und Schemas werden an diesem Punkt hinzugefügt.
  • Wenn eine App so konfiguriert ist, dass mehrere OpenAPI-Dokumente generiert werden, werden Transformatoren für jedes Dokument unabhängig voneinander ausgeführt.

Beispiel: Im folgenden Codeausschnitt:

  • SchemaTransformer2 wird ausgeführt und hat Zugriff auf die Änderungen, die von SchemaTransformer1 vorgenommen wurden.
  • Beide OperationTransformer1 und OperationTransformer2 haben Zugriff auf die Änderungen, die von beiden Schematransformatoren für die Typen vorgenommen werden, die an dem Vorgang beteiligt sind, den sie ausführen.
  • OperationTransformer2 wird nach OperationTransformer1 ausgeführt, sodass es Zugriff auf die Änderungen hat, die von OperationTransformer1 vorgenommen wurden.
  • Sowohl DocumentTransformer1 als auch DocumentTransformer2 werden ausgeführt, nachdem alle Operationen und Schemata dem Dokument hinzugefügt wurden, sodass sie Zugriff auf alle Änderungen haben, die von den Transformatoren für Operationen und Schemata vorgenommen wurden.
  • DocumentTransformer2 wird nach DocumentTransformer1 ausgeführt, sodass es Zugriff auf die Änderungen hat, die von DocumentTransformer1 vorgenommen wurden.
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer<DocumentTransformer1>();
    options.AddSchemaTransformer<SchemaTransformer1>();
    options.AddDocumentTransformer<DocumentTransformer2>();
    options.AddOperationTransformer<OperationTransformer1>();
    options.AddSchemaTransformer<SchemaTransformer2>();
    options.AddOperationTransformer<OperationTransformer2>();
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

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

app.Run();

Verwenden von Dokumenttransformatoren

Dokumenttransformer haben Zugriff auf ein Kontextobjekt, das Folgendes umfasst:

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

if (app.Environment.IsDevelopment())
{
    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();

if (app.Environment.IsDevelopment())
{
    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();

if (app.Environment.IsDevelopment())
{
    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
  • Bedingt auf bestimmte Routen angewandt

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

if (app.Environment.IsDevelopment())
{
    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 vorgenommen wird:

  • Sollte an jedem Schema im Dokument vorgenommen werden oder
  • bedingt auf bestimmte Schemata angewendet werden.

Schematransformatoren haben Zugriff auf ein Kontextobjekt, das Folgendes enthält:

  • Der Name des Dokuments, zu dem das Schema 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();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

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

app.Run();

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

Unterstützung für die Generierung von OpenApiSchemas in Transformatoren

Entwickler können ein Schema für einen C#-Typ mit derselben Logik wie ASP.NET Core OpenAPI-Dokumentgenerierung generieren und dem OpenAPI-Dokument hinzufügen. Das Schema kann dann an anderer Stelle im OpenAPI-Dokument referenziert werden. Diese Funktion ist ab .NET 10 verfügbar.

Der an Dokument-, Vorgangs- und Schematransformatoren übergebene Kontext enthält eine neue GetOrCreateSchemaAsync Methode, mit der ein Schema für einen Typ generiert werden kann. Diese Methode verfügt auch über einen optionalen ApiParameterDescription Parameter, um zusätzliche Metadaten für das generierte Schema anzugeben.

Um das Hinzufügen des Schemas zum OpenAPI-Dokument zu unterstützen, wurde eine Document Eigenschaft zu den Kontexten "Operation" und "Schematransformor" hinzugefügt. Auf diese Weise kann jeder Transformator dem OpenAPI-Dokument mithilfe der Methode des AddComponent Dokuments ein Schema hinzufügen.

Beispiel

Um dieses Feature in einem Dokument, einem Vorgang oder einem Schematransformator zu verwenden, erstellen Sie das Schema mithilfe der GetOrCreateSchemaAsync im Kontext bereitgestellten Methode, und fügen Sie es mithilfe der Methode des AddComponent Dokuments dem OpenAPI-Dokument hinzu.

builder.Services.AddOpenApi(options =>
{
    options.AddOperationTransformer(async (operation, context, cancellationToken) =>
    {
        // Generate schema for error responses
        var errorSchema = await context.GetOrCreateSchemaAsync(typeof(ProblemDetails), null, cancellationToken);
        context.Document?.AddComponent("Error", errorSchema);

        operation.Responses ??= new OpenApiResponses();
        // Add a "4XX" response to the operation with the newly created schema
        operation.Responses["4XX"] = new OpenApiResponse
        {
            Description = "Bad Request",
            Content = new Dictionary<string, OpenApiMediaType>
            {
                ["application/problem+json"] = new OpenApiMediaType
                {
                    Schema = new OpenApiSchemaReference("Error", context.Document)
                }
            }
        };
    });
});

Anpassen der Schemawiederverwendung

Nachdem alle Transformatoren angewendet wurden, macht das Framework einen Durchlauf über das Dokument, um bestimmte Schemas in den Abschnitt components.schemas zu übertragen und ersetzt sie durch $ref-Verweise auf das übertragene Schema. Dadurch wird die Größe des Dokuments reduziert und das Lesen erleichtert.

Die Details dieser Verarbeitung sind kompliziert und können sich in zukünftigen Versionen von .NET ändern, aber im Allgemeinen:

  • Schemas für Klassen-/Datensatz-/Strukturtypen werden durch einen $ref zu einem Schema in components.schemas ersetzt, wenn sie mehr als einmal im Dokument angezeigt werden.
  • Schemas für primitive Typen und Standardauflistungen bleiben inline.
  • Schemas für Enumerationstypen werden immer mit einer $ref durch ein Schema in „components.schemas” ersetzt.

In der Regel ist der Name des Schemas in components.schemas der Name des Klassen-/Datensatz-/Strukturtyps, aber unter bestimmten Umständen muss ein anderer Name verwendet werden.

ASP.NET Core ermöglicht es Ihnen, anzupassen, welche Schemas durch ein $ref in einem Schema in components.schemas mithilfe der Eigenschaft CreateSchemaReferenceId von OpenApiOptions ersetzt werden. Diese Eigenschaft ist ein Delegat, der ein JsonTypeInfo-Objekt nimmt und den Namen des Schemas in components.schemas zurückgibt, das für diesen Typ verwendet werden soll. Das Framework bietet eine Standardimplementierung des Delegaten CreateDefaultSchemaReferenceId, der den Namen des Typs verwendet. Sie können diesen jedoch durch Ihre eigene Implementierung ersetzen.

Als einfaches Beispiel für diese Anpassung können Sie festlegen, dass immer Inline-Enumerationsschemas verwendet werden. Dies wird erreicht, indem CreateSchemaReferenceId auf einen Delegaten gesetzt wird, der für Enumerationstypen null zurückgibt und sonst den Wert aus der Standardimplementierung liefert. Der folgende Code veranschaulicht folgendes:

builder.Services.AddOpenApi(options =>
{
    // Always inline enum schemas
    options.CreateSchemaReferenceId = (type) =>
        type.Type.IsEnum ? null : OpenApiOptions.CreateDefaultSchemaReferenceId(type);
});

Zusätzliche Ressourcen

Transformatoren für OpenAPI-Dokumente

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:

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

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

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

app.Run();

Ausführungsreihenfolge für Transformer

Transformatoren werden wie folgt ausgeführt:

  • Schematransformatoren werden ausgeführt, wenn ein Schema für das Dokument registriert ist. Schematransformatoren werden in der Reihenfolge ausgeführt, in der sie hinzugefügt wurden. Alle Schemas werden dem Dokument hinzugefügt, bevor eine Vorgangsverarbeitung erfolgt, sodass alle Schematransformatoren vor Vorgangstransformatoren ausgeführt werden.
  • Vorgangstransformatoren werden ausgeführt, wenn dem Dokument ein Vorgang hinzugefügt wird. Betriebstransformatoren werden in der Reihenfolge ausgeführt, in der sie hinzugefügt wurden. Alle Vorgänge werden dem Dokument hinzugefügt, bevor Dokumenttransformatoren ausgeführt werden.
  • Dokumenttransformatoren werden ausgeführt, wenn das Dokument generiert wird. Dies ist der letzte Durchlauf des Dokuments, und alle Vorgänge und Schemas wurden an diesem Punkt hinzugefügt.
  • Wenn eine App so konfiguriert ist, dass mehrere OpenAPI-Dokumente generiert werden, werden Transformatoren für jedes Dokument unabhängig voneinander ausgeführt.

Beispiel: Im folgenden Codeausschnitt:

  • SchemaTransformer2 wird ausgeführt und hat Zugriff auf die Änderungen, die von SchemaTransformer1 vorgenommen wurden.
  • OperationTransformer1 und OperationTransformer2 haben Zugriff auf die Änderungen, die von beiden Schematransformatoren für die Typen vorgenommen wurden, die an dem Vorgang beteiligt sind, zu dem sie aufgerufen wurden, um ihn zu verarbeiten.
  • OperationTransformer2 wird nach OperationTransformer1 ausgeführt, sodass es Zugriff auf die Änderungen hat, die von OperationTransformer1 vorgenommen wurden.
  • Sowohl DocumentTransformer1 als auch DocumentTransformer2 werden ausgeführt, nachdem alle Operationen und Schemata dem Dokument hinzugefügt wurden, sodass sie Zugriff auf alle Änderungen haben, die von den Transformatoren für Operationen und Schemata vorgenommen wurden.
  • DocumentTransformer2 wird nach DocumentTransformer1 ausgeführt, sodass es Zugriff auf die Änderungen hat, die von DocumentTransformer1 vorgenommen wurden.
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer<DocumentTransformer1>();
    options.AddSchemaTransformer<SchemaTransformer1>();
    options.AddDocumentTransformer<DocumentTransformer2>();
    options.AddOperationTransformer<OperationTransformer1>();
    options.AddSchemaTransformer<SchemaTransformer2>();
    options.AddOperationTransformer<OperationTransformer2>();
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

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

app.Run();

Verwenden von Dokumenttransformatoren

Dokumenttransformer haben Zugriff auf ein Kontextobjekt, das Folgendes umfasst:

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

if (app.Environment.IsDevelopment())
{
    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();

if (app.Environment.IsDevelopment())
{
    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();

if (app.Environment.IsDevelopment())
{
    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
  • Bedingt auf bestimmte Routen angewandt

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

if (app.Environment.IsDevelopment())
{
    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 vorgenommen wird:

  • Sollte an jedem Schema im Dokument vorgenommen werden oder
  • bedingt auf bestimmte Schemata angewendet werden.

Schematransformatoren haben Zugriff auf ein Kontextobjekt, das Folgendes enthält:

  • Der Name des Dokuments, zu dem das Schema 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();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

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

app.Run();

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

Anpassen der Schemawiederverwendung

Nachdem alle Transformatoren angewendet wurden, macht das Framework einen Durchlauf über das Dokument, um bestimmte Schemas in den Abschnitt components.schemas zu übertragen und ersetzt sie durch $ref-Verweise auf das übertragene Schema. Dadurch wird die Größe des Dokuments reduziert und das Lesen erleichtert.

Die Details dieser Verarbeitung sind kompliziert und können sich in zukünftigen Versionen von .NET ändern, aber im Allgemeinen:

  • Schemas für Klassen-/Datensatz-/Strukturtypen werden durch einen $ref zu einem Schema in components.schemas ersetzt, wenn sie mehr als einmal im Dokument angezeigt werden.
  • Schemas für primitive Typen und Standardauflistungen bleiben inline.
  • Schemas für Enumerationstypen werden immer mit einer $ref durch ein Schema in „components.schemas” ersetzt.

In der Regel ist der Name des Schemas in components.schemas der Name des Klassen-/Datensatz-/Strukturtyps, aber unter bestimmten Umständen muss ein anderer Name verwendet werden.

ASP.NET Core ermöglicht es Ihnen, anzupassen, welche Schemas durch ein $ref in einem Schema in components.schemas mithilfe der Eigenschaft CreateSchemaReferenceId von OpenApiOptions ersetzt werden. Diese Eigenschaft ist ein Delegat, der ein JsonTypeInfo-Objekt nimmt und den Namen des Schemas in components.schemas zurückgibt, das für diesen Typ verwendet werden soll. Das Framework bietet eine Standardimplementierung des Delegaten CreateDefaultSchemaReferenceId, der den Namen des Typs verwendet. Sie können diesen jedoch durch Ihre eigene Implementierung ersetzen.

Als einfaches Beispiel für diese Anpassung können Sie festlegen, dass immer Inline-Enumerationsschemas verwendet werden. Dies wird erreicht, indem CreateSchemaReferenceId auf einen Delegaten gesetzt wird, der für Enumerationstypen null zurückgibt und sonst den Wert aus der Standardimplementierung liefert. Der folgende Code zeigt, wie Dies geschieht:

builder.Services.AddOpenApi(options =>
{
    // Always inline enum schemas
    options.CreateSchemaReferenceId = (type) =>
        type.Type.IsEnum ? null : OpenApiOptions.CreateDefaultSchemaReferenceId(type);
});

Zusätzliche Ressourcen