Compartilhar via


Introdução a Microsoft.AspNetCore.OpenApi

O pacote Microsoft.AspNetCore.OpenApi fornece suporte interno para a geração de documentos OpenAPI no ASP.NET Core. O pacote :

  • É compatível com o AoT nativo.
  • Aproveita o JSsuporte ao esquema ON fornecido por System.Text.Json.
  • Fornece uma API de transformadores para modificar documentos gerados.
  • Dá suporte ao gerenciamento de vários documentos OpenAPI em um único aplicativo.

Instalação do pacote

Instalar o pacote Microsoft.AspNetCore.OpenApi:

Execute o seguinte comando no Console do Gerenciador de Pacotes:

Install-Package Microsoft.AspNetCore.OpenApi -IncludePrerelease

Configurar a geração de documentos OpenAPI

O seguinte código:

  • Adiciona serviços OpenAPI.
  • Habilita o ponto de extremidade para exibir o documento OpenAPI no formato JSON.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Inicie o aplicativo e navegue até https://localhost:<port>/openapi/v1.json para exibir o documento OpenAPI gerado.

A importância dos nomes de documentos

Cada documento OpenAPI em um aplicativo tem um nome exclusivo. O nome do documento padrão registrado é v1.

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

Para modificar o nome do documento, passe o nome como um parâmetro para a chamada AddOpenApi.

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

O nome do documento aparece em vários locais na implementação do OpenAPI.

Ao buscar o documento OpenAPI gerado, o nome do documento é fornecido como o argumento de parâmetro documentName na solicitação. As solicitações a seguir resolvem os documentos v1 e internal.

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

Opções para personalizar a geração de documentos OpenAPI

As seções a seguir demonstram como personalizar a geração de documentos OpenAPI.

Personalizar a versão do OpenAPI de um documento gerado

Por padrão, a geração de documentos OpenAPI cria um documento compatível com a v3.0 da especificação OpenAPI. O código a seguir demonstra como modificar a versão padrão do documento OpenAPI:

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

Personalizar a rota do ponto de extremidade OpenAPI

Por padrão, o ponto de extremidade OpenAPI registrado por meio de uma chamada para MapOpenApi expõe o documento no ponto de extremidade /openapi/{documentName}.json. O código a seguir demonstra como personalizar a rota na qual o documento OpenAPI está registrado:

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

Observação: é possível, mas não recomendado, remover o parâmetro de rota documentName da rota do ponto de extremidade. Quando o parâmetro de rota documentName é removido da rota do ponto de extremidade, a estrutura tenta resolver o nome do documento a partir do parâmetro de consulta. Não fornecer o documentName na rota ou na consulta pode resultar em um comportamento inesperado.

Personalizar o ponto de extremidade OpenAPI

Como o documento OpenAPI é servido por meio de um ponto de extremidade do manipulador de rota, qualquer personalização disponível para ponto de extremidade mínimos padrão estará disponível para o ponto de extremidade OpenAPI.

Personalizar pontos de extremidade OpenAPI com metadados de ponto de extremidade

A lista a seguir mostra os metadados de ponto de extremidade que é usado para personalizar o documento OpenAPI gerado:

Para saber mais sobre como personalizar o documento OpenAPI gerado modificando metadados de ponto de extremidade, consulte Como usar o OpenAPI em aplicativos de API mínimos.

Limitar o acesso a documentos OpenAPI a usuários autorizados

O ponto de extremidade OpenAPI não permite nenhuma verificação de autorização por padrão. No entanto, é possível limitar o acesso ao documento OpenAPI. Por exemplo, no código a seguir, o acesso ao documento OpenAPI é limitado àqueles com a função 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();

Documento OpenAPI gerado pelo cache

O documento OpenAPI é gerado novamente sempre que uma solicitação ao ponto de extremidade OpenAPI é enviada. A regeneração permite que os transformadores incorporem o estado dinâmico do aplicativo em sua operação. Por exemplo, regenerar uma solicitação com detalhes do contexto HTTP. Quando aplicável, o documento OpenAPI pode ser armazenado em cache para evitar a execução do pipeline de geração de documentos em cada solicitação 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();

Transformadores de documento OpenAPI

Esta seção demonstra como personalizar documentos OpenAPI com transformadores.

Personalizar documentos OpenAPI com transformadores

Os transformadores fornecem uma API para modificar o documento OpenAPI com personalizações definidas pelo usuário. Transformadores são úteis para cenários como:

  • Adicionar parâmetros a todas as operações em um documento.
  • Modificar descrições para parâmetros ou operações.
  • Adicionar informações de nível superior ao documento OpenAPI.

Os transformadores se enquadram em duas categorias:

  • Os transformadores de documento têm acesso a todo o documento OpenAPI. Eles podem ser usados para fazer modificações globais no documento.
  • Os transformadores de operação se aplicam a cada operação individual. Cada operação individual é uma combinação de caminho e método HTTP. Elas podem ser usadas para modificar parâmetros ou respostas em pontos de extremidade.

Os transformadores podem ser registrados no documento por meio da chamada UseTransformer no objeto OpenApiOptions. O seguinte trecho de código mostra diferentes maneiras de registrar transformadores no documento:

  • Registre um transformador de documento usando um delegado.
  • Registre um transformador de documento usando uma instância de IOpenApiDocumentTransformer.
  • Registre um transformador de documento usando um IOpenApiDocumentTransformer ativado por DI.
  • Registrar um transformador de operação usando um delegado.
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();

Ordem de execução para transformadores

Os transformadores são executados na ordem "primeiro a entrar, primeiro a sair" com base no registro. No trecho de código a seguir, o transformador de documento tem acesso às modificações feitas pelo transformador de operação:

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

Usar transformadores de documento

Os transformadores de documento têm acesso a um objeto de contexto que inclui:

  • O nome do documento que está sendo modificado.
  • A lista de ApiDescriptionGroups associados a esse documento.
  • O IServiceProvider usado na geração de documentos.

Os transformadores de documento também podem alterar o documento OpenAPI gerado. O exemplo a seguir demonstra um transformador de documento que adiciona algumas informações sobre a API ao documento 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();

Os transformadores de documento ativados pelo serviço podem utilizar instâncias de DI para modificar o aplicativo. O exemplo a seguir demonstra um transformador de documento que usa o serviço IAuthenticationSchemeProvider da camada de autenticação. Ele verifica se algum esquema relacionado ao portador JWT está registrado no aplicativo e os adiciona ao nível superior do documento 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;
        }
    }
}

Os transformadores de documento são exclusivos da instância do documento à qual estão associados. No exemplo a seguir, um transformador:

  • Registra requisitos relacionados à autenticação no documento internal.
  • Deixa o documento public não modificado.
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>()
                });
            }
        }
    }
}

Usar transformadores de operação

As operações são combinações exclusivas de métodos e caminhos HTTP em um documento OpenAPI. Os transformadores de operação são úteis quando uma modificação:

  • Deve ser feita para cada ponto de extremidade em um aplicativo ou
  • Aplicada condicionalmente a determinadas rotas.

Os transformadores de operação têm acesso a um objeto de contexto que contém:

  • O nome do documento ao qual a operação pertence.
  • O ApiDescription associado à operação.
  • O IServiceProvider usado na geração de documentos.

Por exemplo, o transformador de operação a seguir adiciona 500 como um código de status de resposta compatível com todas as operações no documento.

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

Usar o documento OpenAPI gerado

Os documentos OpenAPI podem se conectar a um amplo ecossistema de ferramentas existentes para teste, documentação e desenvolvimento local.

Usar a interface do usuário do Swagger para testes ad hoc locais

Por padrão, o pacote Microsoft.AspNetCore.OpenApi não é fornecido com suporte interno para visualizar ou interagir com o documento OpenAPI. Ferramentas populares para visualizar ou interagir com o documento OpenAPI incluem a interface do usuário do Swagger e o ReDoc. A interface do usuário do Swagger e o ReDoc podem ser integrados em um aplicativo de várias maneiras. Os editores como o Visual Studio e o VS Code oferecem extensões e experiências internas para teste em um documento OpenAPI.

O pacote Swashbuckle.AspNetCore.SwaggerUi fornece um pacote de ativos da Web da interface do usuário do Swagger para uso em aplicativos. Esse pacote pode ser usado para renderizar uma interface do usuário para o documento gerado. Para configurar isso, instale o pacote Swashbuckle.AspNetCore.SwaggerUi.

Habilite o middleware swagger-ui com uma referência à rota OpenAPI registrada anteriormente. Para limitar a divulgação de informações e a vulnerabilidade de segurança, habilite apenas a interface do usuário do Swagger em ambientes de desenvolvimento.

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

Usando Escalar para documentação de API interativa

Escalar é uma UI de documento interativo de código aberto para OpenAPI. Scalar pode ser integrado ao ponto de extremidade OpenAPI fornecido pelo ASP.NET Core. Para configurar o Scalar, instale o pacote 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();

Executar lint de documentos OpenAPI gerados com Spectral

Spectral é um linter de documento OpenAPI de código aberto. O Spectral pode ser incorporado ao build do aplicativo para verificar a qualidade dos documentos OpenAPI gerados. Instale o Spectral de acordo com as instruções de instalação do pacote.

Para aproveitar o Spectral, instale o pacote Microsoft.Extensions.ApiDescription.Server para habilitar a geração de documentos OpenAPI no tempo do build.

Habilite a geração de documentos em tempo de build definindo as seguintes propriedades no arquivo .csproj do aplicativo”:

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

Execute dotnet build para gerar o documento.

dotnet build

Crie um arquivo .spectral.yml com o conteúdo a seguir.

extends: ["spectral:oas"]

Depois, execute spectral lint no arquivo gerado.

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)