Partager via


Bien démarrer avec Microsoft.AspNetCore.OpenApi

Le package Microsoft.AspNetCore.OpenApi fournit une prise en charge intégrée de la génération de documents OpenAPI dans ASP.NET Core. Package  :

  • Est compatible avec AoT natif.
  • Tire parti de la JSprise en charge du schéma ON fournie par System.Text.Json.
  • Fournit une API de transformateurs pour la modification de documents générés.
  • Prend en charge la gestion de plusieurs documents OpenAPI dans une seule application.

Installation de package

Installez le package Microsoft.AspNetCore.OpenApi :

Exécutez la commande suivante à partir de la console Gestionnaire de package:

Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease

Configurer la génération de document OpenAPI

Le code suivant :

  • Ajoute des services OpenAPI.
  • Active le point de terminaison pour afficher le document OpenAPI au format JSON.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Lancez l’application et accédez à https://localhost:<port>/openapi/v1.json pour afficher le document OpenAPI généré.

L’importance du nom des documents

Chaque document OpenAPI dans une application a un nom unique. Le nom du document par défaut inscrit est v1.

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

Le nom du document peut être modifié en passant le nom en tant que paramètre à l’appel AddOpenApi.

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

Le nom du document s’affiche à plusieurs endroits dans l’implémentation OpenAPI.

Lors de l’extraction du document OpenAPI généré, le nom du document est fourni en tant qu’argument de paramètre documentName dans la requête. Les requêtes suivantes résolvent les documents v1 et internal.

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

Options pour personnaliser la génération de document OpenAPI

Les sections suivantes montrent comment personnaliser la génération de document OpenAPI.

Personnaliser la version OpenAPI d’un document généré

Par défaut, la génération de document OpenAPI crée un document conforme à la version 3.0 de la spécification OpenAPI. Le code suivant montre comment modifier la version par défaut du document OpenAPI :

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

Personnaliser l’itinéraire du point de terminaison OpenAPI

Par défaut, le point de terminaison OpenAPI inscrit via un appel à MapOpenApi expose le document au niveau du point de terminaison /openapi/{documentName}.json. Le code suivant montre comment personnaliser l’itinéraire au niveau duquel le document OpenAPI est inscrit :

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

Remarque : bien que ce ne soit pas recommandé, il est possible de supprimer le paramètre d’itinéraire documentName de l’itinéraire du point de terminaison. Lorsque le paramètre d’itinéraire documentName est supprimé de l’itinéraire du point de terminaison, l’infrastructure tente de résoudre le nom du document à partir du paramètre de requête. Ne pas fournir le documentName dans l’itinéraire ou la requête peut entraîner un comportement inattendu.

Personnaliser le point de terminaison OpenAPI

Étant donné que le document OpenAPI est fourni via un point de terminaison de gestionnaire de routage, toute personnalisation disponible pour les points de terminaison minimaux standard l’est aussi pour le point de terminaison OpenAPI.

Personnaliser les points de terminaison OpenAPI avec des métadonnées de point de terminaison

La liste suivante montre les métadonnées de point de terminaison utilisées pour personnaliser le document OpenAPI généré :

Pour en savoir plus sur la personnalisation du document OpenAPI généré en modifiant les métadonnées de point de terminaison, consultez Comment utiliser OpenAPI dans les applications API minimales.

Limiter l’accès au document OpenAPI aux utilisateurs autorisés

Par défaut, le point de terminaison OpenAPI n’active aucune vérification des autorisations. Toutefois, il est possible de limiter l’accès au document OpenAPI. Par exemple, dans le code suivant, l’accès au document OpenAPI est limité aux utilisateurs disposant du rôle tester :

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

var builder = WebApplication.CreateBuilder();

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

var app = builder.Build();

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

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

app.Run();

Mettre en cache le document OpenAPI généré

Le document OpenAPI est régénéré chaque fois qu’une requête est envoyée au point de terminaison OpenAPI. La régénération permet aux transformateurs d’incorporer l’état de l’application dynamique dans leur fonctionnement. Par exemple,vous pouvez régénérer une requête avec les détails du contexte HTTP. Le cas échéant, le document OpenAPI peut être mis en cache pour éviter d’exécuter le pipeline de génération de documents sur chaque requête HTTP.

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

var builder = WebApplication.CreateBuilder();

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

var app = builder.Build();

app.UseOutputCache();

app.MapOpenApi()
    .CacheOutput();

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

app.Run();

Transformateurs de documents OpenAPI

Cette section montre comment personnaliser des documents OpenAPI avec des transformateurs.

Personnaliser des documents OpenAPI avec des transformateurs

Les transformateurs fournissent une API permettant de modifier le document OpenAPI avec des personnalisations définies par l’utilisateur. Les transformateurs sont utiles dans des scénarios tels que les suivants :

  • Ajout de paramètres à toutes les opérations d’un document.
  • Modification des descriptions des paramètres ou des opérations.
  • Ajout d’informations de premier niveau au document OpenAPI.

Les transformateurs se répartissent en deux catégories :

  • Les transformateurs de documents ont accès à l’intégralité du document OpenAPI. Vous pouvez les utiliser pour apporter des modifications globales au document.
  • Les transformateurs d’opérations s’appliquent à chaque opération individuelle. Chaque opération individuelle combine un chemin d’accès et une méthode HTTP. Vous pouvez les utiliser pour modifier des paramètres ou des réponses sur des points de terminaison.

Les transformateurs peuvent être inscrits sur le document via l’appel UseTransformer sur l’objet OpenApiOptions. L’extrait de code suivant montre différentes façons d’inscrire des transformateurs sur le document :

  • Inscrire un transformateur de document à l’aide d’un délégué.
  • Inscrire un transformateur de document à l’aide d’une instance de IOpenApiDocumentTransformer.
  • Inscrire un transformateur de document à l’aide d’un IOpenApiDocumentTransformer avec injection de dépendance activée.
  • Inscrire un transformateur d’opération à l’aide d’un délégué.
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.UseTransformer((document, context, cancellationToken) 
                             => Task.CompletedTask);
    options.UseTransformer(new MyDocumentTransformer());
    options.UseTransformer<MyDocumentTransformer>();
    options.UseOperationTransformer((operation, context, cancellationToken)
                            => Task.CompletedTask);
});

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Ordre d’exécution pour les transformateurs

Les transformateurs s’exécutent dans un ordre de premier entré, premier sorti (FIFO) en fonction de l’inscription. Dans l’extrait de code suivant, le transformateur de document a accès aux modifications apportées par le transformateur d’opération :

var builder = WebApplication.CreateBuilder();

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

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Utiliser des transformateurs de documents

Les transformateurs de documents ont accès à un objet de contexte qui inclut :

  • Le nom du document en cours de modification.
  • La liste des ApiDescriptionGroups associés à ce document.
  • IServiceProvider utilisé dans la génération de document.

Les transformateurs de documents peuvent également muter le document OpenAPI généré. L’exemple suivant illustre un transformateur de document qui ajoute des informations sur l’API au document OpenAPI.

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

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.UseTransformer((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();

Les transformateurs de documents activés par le service peuvent utiliser des instances de DI pour modifier l’application. L’exemple suivant illustre un transformateur de document qui utilise le service IAuthenticationSchemeProvider à partir de la couche d’authentification. Il vérifie si des schémas liés au porteur JWT sont inscrits dans l’application et les ajoute au premier niveau du document OpenAPI :

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

var builder = WebApplication.CreateBuilder();

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

builder.Services.AddOpenApi(options =>
{
    options.UseTransformer<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;
        }
    }
}

Les transformateurs de documents sont uniques à l’instance de document à laquelle ils sont associés. Dans l’exemple suivant, un transformateur :

  • Inscrit les exigences relatives à l’authentification dans le document internal.
  • Laisse le document public non modifié.
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.UseTransformer<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>()
                });
            }
        }
    }
}

Utiliser des transformateurs d’opérations

Les opérations sont des combinaisons uniques de chemins d’accès HTTP et de méthodes dans un document OpenAPI. Les transformateurs d’opérations sont utiles lorsqu’une modification :

  • doit être effectuée sur chaque point de terminaison d’une application ;
  • ou a été appliquée de manière conditionnelle à certains itinéraires.

Les transformateurs d’opération ont accès à un objet de contexte qui contient :

  • Le nom du document auquel appartient l’opération.
  • ApiDescription associé à l'opération.
  • IServiceProvider utilisé dans la génération de document.

Par exemple, le transformateur d’opération suivant ajoute 500 comme code d’état de réponse pris en charge par toutes les opérations du document.

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

Utilisation du document OpenAPI généré

Les documents OpenAPI peuvent se connecter à un large écosystème d’outils existants pour les tests, la documentation et le développement local.

Utilisation de l’interface utilisateur de Swagger pour les tests ad hoc locaux

Par défaut, le package Microsoft.AspNetCore.OpenApi n’est pas fourni avec la prise en charge intégrée de la visualisation du document OpenAPI ou de l’interaction avec celui-ci. Parmi les outils populaires permettant de visualiser le document OpenAPI ou d’interagir avec celui-ci, citons Swagger UI et ReDoc. Vous pouvez intégrer Swagger UI et ReDoc dans une application de plusieurs façons. Des éditeurs tels que Visual Studio et VS Code proposent des extensions et des expériences intégrées pour effectuer des tests sur un document OpenAPI.

Le package Swashbuckle.AspNetCore.SwaggerUi fournit une offre groupée des ressources web de Swagger UI à utiliser dans les applications. Ce package peut être utilisé pour afficher une interface utilisateur pour le document généré. Pour configurer cela, installez le package Swashbuckle.AspNetCore.SwaggerUi.

Activez l’intergiciel swagger-ui avec une référence à l’itinéraire OpenAPI inscrit précédemment. Pour limiter la divulgation d’informations et les vulnérabilités de sécurité, activez uniquement Swagger UI dans les environnements de développement.

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

var app = builder.Build();

app.MapOpenApi();
if (app.Environment.IsDevelopment())
{
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/openapi/v1.json", "v1");
    });

}

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

app.Run();

Utilisation de Scalar pour la documentation d’API interactive

Scalar est une interface utilisateur de document interactif open source pour OpenAPI. Scalar peut s’intégrer au point de terminaison OpenAPI fourni par ASP.NET Core. Pour configurer Scalar, installez le package Scalar.AspNetCore.

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

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

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

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

app.Run();

Linting de documents OpenAPI générés avec Spectral

Spectral est un linter de documents OpenAPI open source. Vous pouvez incorporer Spectral dans une build d’application pour vérifier la qualité des documents OpenAPI générés. Installez Spectral en fonction des instructions d’installation du package.

Pour tirer parti de Spectral, installez le package Microsoft.Extensions.ApiDescription.Server pour activer la génération de documents OpenAPI au moment de la build.

Activez la génération de document au moment de la création de la build en définissant les propriétés suivantes dans le fichier .csproj de votre application :

<PropertyGroup>
    <OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
    <OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
</PropertyGroup>

Exécutez dotnet build pour générer le document.

dotnet build

Créez un fichier .spectral.yml avec le contenu suivant.

extends: ["spectral:oas"]

Exécutez spectral lint sur le fichier généré.

spectral lint WebMinOpenApi.json
...

The output shows any issues with the OpenAPI document.

```output
1:1  warning  oas3-api-servers       OpenAPI "servers" must be present and non-empty array.
3:10  warning  info-contact           Info object must have "contact" object.                        info
3:10  warning  info-description       Info "description" must be present and non-empty string.       info
9:13  warning  operation-description  Operation "description" must be present and non-empty string.  paths./.get
9:13  warning  operation-operationId  Operation must have "operationId".                             paths./.get

✖ 5 problems (0 errors, 5 warnings, 0 infos, 0 hints)