Gerar documentos OpenAPI
O pacote Microsoft.AspNetCore.OpenApi
fornece suporte interno para a geração de documentos OpenAPI no ASP.NET Core. O pacote fornece os seguintes recursos:
- Suporte para geração de documentos OpenAPI em tempo de execução e acesso a esses documentos por meio de um ponto de extremidade no aplicativo.
- Suporte para APIs "transformadoras" que permitem modificar o documento gerado.
- Suporte para gerar vários documentos OpenAPI a partir de um único aplicativo.
- Aproveita o suporte ao esquema JSON fornecido por
System.Text.Json
. - É compatível com o AoT nativo.
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
Para adicionar suporte para a geração de documentos OpenAPI no momento da compilação, instale o pacote Microsoft.Extensions.ApiDescription.Server
:
Execute o seguinte comando no Console do Gerenciador de Pacotes:
Install-Package Microsoft.Extensions.ApiDescription.Server -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.
Incluir metadados OpenAPI em um aplicativo Web ASP.NET
Inclusão de metadados OpenAPI para pontos de extremidade
O ASP.NET coleta metadados dos pontos de extremidade do aplicativo Web e os usa para gerar um documento OpenAPI.
Em aplicativos baseados em controlador, os metadados são coletados de atributos como [EndpointDescription]
, [HttpPost]
e [Produces]
.
Em APIs mínimas, os metadados podem ser coletados de atributos, mas também podem ser definidos usando métodos de extensão e outras estratégias, como retornar TypedResults de manipuladores de rotas.
A tabela a seguir fornece uma visão geral dos metadados coletados e das estratégias para configurá-los.
Metadados | Atributo | Método de extensão | Outras estratégias |
---|---|---|---|
summary | [EndpointSummary] |
WithSummary | |
descrição | [EndpointDescription] |
WithDescription | |
tags | [Tags] |
WithTags | |
operationId | [EndpointName] |
WithName | |
parâmetros | [FromQuery] , [FromRoute] , [FromHeader] , [FromForm] |
||
Descrição do parâmetro | [EndpointDescription] |
||
requestBody | [FromBody] |
Accepts | |
responses | [Produces] |
Produces, ProducesProblem | TypedResults |
Exclusão de pontos de extremidade | [ExcludeFromDescription] , [ApiExplorerSettings] |
ExcludeFromDescription |
O ASP.NET Core não coleta metadados de comentários de documentação XML.
As seções a seguir demonstram como incluir metadados em um aplicativo para personalizar o documento OpenAPI gerado.
Resumo e descrição
O resumo e a descrição do ponto de extremidade podem ser definidos usando os atributos [EndpointSummary]
e [EndpointDescription]
ou, em APIs mínimas, usando os métodos de extensão WithSummary e WithDescription.
A amostra a seguir demonstra as diferentes estratégias para configurar resumos e descrições.
Observe que os atributos são colocados no método delegado e não no método app.MapGet.
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
A OpenAPI dá suporte à especificação de marcas em cada ponto de extremidade como uma forma de categorização.
Em APIs mínimas, as marcas podem ser definidas usando o atributo [Tags]
ou o método de extensão WithTags.
O exemplo a seguir demonstra as diferentes estratégias para configurar marcas.
app.MapGet("/extension-methods", () => "Hello world!")
.WithTags("todos", "projects");
app.MapGet("/attributes",
[Tags("todos", "projects")]
() => "Hello world!");
operationId
O OpenAPI oferece suporte a um operationId em cada ponto de extremidade como um identificador ou nome exclusivo para a operação.
Em APIs mínimas, o operationId pode ser definido usando o atributo [EndpointName]
ou o método de extensão WithName.
O exemplo a seguir demonstra as diferentes estratégias para configurar o operationId.
app.MapGet("/extension-methods", () => "Hello world!")
.WithName("FromExtensionMethods");
app.MapGet("/attributes",
[EndpointName("FromAttributes")]
() => "Hello world!");
parâmetros
O OpenAPI oferece suporte à anotação de caminho, string de consulta, cabeçalho e parâmetros cookie que são consumidos por uma API.
A estrutura infere os tipos de parâmetros de solicitação automaticamente com base na assinatura do manipulador de rotas.
O atributo [EndpointDescription]
pode ser usado para fornecer uma descrição para um parâmetro.
O exemplo a seguir demonstra como definir uma descrição para um parâmetro.
app.MapGet("/attributes",
([Description("This is a description.")] string name) => "Hello world!");
Descreva o corpo da solicitação
O requestBody
campo no OpenAPI descreve o corpo de uma solicitação que um cliente de API pode enviar ao servidor, incluindo os tipos de conteúdo suportados e o esquema para o conteúdo do corpo.
Quando o método do manipulador de endpoint aceita parâmetros vinculados ao corpo da solicitação, ASP.NET Core gera um correspondente requestBody
para a operação no documento OpenAPI. Os metadados para o corpo da solicitação também podem ser especificados usando atributos ou métodos de extensão. Metadados adicionais podem ser definidos com um transformador de documento ou transformador de operação.
Se o endpoint não definir nenhum parâmetro vinculado ao corpo da solicitação, mas, em vez disso, consumir o corpo da solicitação diretamente, o HttpContext ASP.NET Core fornecerá mecanismos para especificar metadados do corpo da solicitação. Esse é um cenário comum para endpoints que processam o corpo da solicitação como um fluxo.
Alguns metadados do corpo da solicitação podem ser determinados a FromBody
partir dos parâmetros ou FromForm
do método do manipulador de rotas.
Uma descrição para o corpo da solicitação pode ser definida com um [Description]
atributo no parâmetro com FromBody
ou FromForm
.
Se o FromBody
parâmetro não for anulável e EmptyBodyBehavior não estiver definido como Allow no FromBody
atributo, o corpo da solicitação será necessário e o required
campo do requestBody
será definido como true
no documento OpenAPI gerado.
Os corpos do formulário são sempre necessários e foram required
definidos como true
.
Use um transformador de documento ou um transformador de operação para definir os example
campos , examples
, ou encoding
ou para adicionar extensões de especificação para o corpo da solicitação no documento OpenAPI gerado.
Outros mecanismos para definir metadados do corpo da solicitação dependem do tipo de aplicativo que está sendo desenvolvido e são descritos nas seções a seguir.
Os tipos de conteúdo para o corpo da solicitação no documento OpenAPI gerado são determinados a partir do tipo do parâmetro que está associado ao corpo da solicitação ou especificado com o Accepts método de extensão.
Por padrão, o tipo de conteúdo de um FromBody
parâmetro será application/json
e o tipo de conteúdo do FromForm
(s) parâmetro(s) será multipart/form-data
ou application/x-www-form-urlencoded
.
O suporte para esses tipos de conteúdo padrão é integrado às APIs mínimas e outros tipos de conteúdo podem ser tratados usando a associação personalizada. Consulte o tópico Associação personalizada da documentação de APIs mínimas para obter mais informações.
Há várias maneiras de especificar um tipo de conteúdo diferente para o corpo da solicitação.
Se o tipo do FromBody
parâmetro implementar IEndpointParameterMetadataProvider, ASP.NET Core usará essa interface para determinar os tipos de conteúdo no corpo da solicitação.
A estrutura usa o PopulateMetadata método dessa interface para definir os tipos de conteúdo e o tipo de conteúdo do corpo do corpo da solicitação. Por exemplo, uma Todo
classe que aceita application/xml
ou content-type text/xml
pode usar IEndpointParameterMetadataProvider para fornecer essas informações à estrutura.
public class Todo : IEndpointParameterMetadataProvider
{
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
builder.Metadata.Add(new AcceptsMetadata(["application/xml", "text/xml"], typeof(Todo)));
}
}
O Accepts método de extensão também pode ser usado para especificar o tipo de conteúdo do corpo da solicitação.
No exemplo a seguir, o ponto de extremidade aceita um objeto Todo
no corpo da solicitação com um tipo de conteúdo esperado de application/xml
.
app.MapPut("/todos/{id}", (int id, Todo todo) => ...)
.Accepts<Todo>("application/xml");
Como application/xml
não é um tipo de conteúdo interno, a Todo
classe deve implementar a IBindableFromHttpContext<TSelf> interface para fornecer uma associação personalizada para o corpo da solicitação. Por exemplo:
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());
}
Se o ponto de extremidade não definir nenhum parâmetro associado ao corpo da solicitação, use o Accepts método de extensão para especificar o tipo de conteúdo que o ponto de extremidade aceita.
Se você especificar <AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepts%2A> várias vezes, somente os metadados do último serão usados – eles não serão combinados.
Descrever os tipos de resposta
O OpenAPI dá suporte ao fornecimento de uma descrição das respostas retornadas de uma API. ASP.NET Core fornece várias estratégias para definir os metadados de resposta de um ponto de extremidade. Os metadados de resposta que podem ser definidos incluem o código de status, o tipo do corpo da resposta e os tipos de conteúdo de uma resposta. As respostas no OpenAPI podem ter metadados adicionais, como descrição, cabeçalhos, links e exemplos. Esses metadados adicionais podem ser definidos com um transformador de documento ou um transformador de operação.
Os mecanismos específicos para definir metadados de resposta dependem do tipo de aplicativo que está sendo desenvolvido.
Em aplicativos de API mínimos, ASP.NET Core pode extrair os metadados de resposta adicionados por métodos de extensão no ponto de extremidade, atributos no manipulador de rotas e o tipo de retorno do manipulador de rotas.
- O Produces método de extensão pode ser usado no ponto de extremidade para especificar o código de status, o tipo do corpo da resposta e os tipos de conteúdo de uma resposta de um ponto de extremidade.
- O
[ProducesResponseType]
atributo or ProducesResponseTypeAttribute<T> pode ser usado para especificar o tipo do corpo da resposta. - Um manipulador de rotas pode ser usado para retornar um tipo que implementa IEndpointMetadataProvider para especificar o tipo e os tipos de conteúdo do corpo da resposta.
- O ProducesProblem método de extensão no ponto de extremidade pode ser usado para especificar o código de status e os tipos de conteúdo de uma resposta de erro.
Observe que os Produces métodos de extensão e ProducesProblem são suportados em ambos e RouteHandlerBuilder em RouteGroupBuilder. Isso permite, por exemplo, que um conjunto comum de respostas de erro seja definido para todas as operações em um grupo.
Quando não especificado por uma das estratégias anteriores, o:
- O código de status para a resposta é padronizado para 200.
- O esquema para o corpo da resposta pode ser inferido do tipo de retorno implícito ou explícito do método de ponto de extremidade, por exemplo, de
T
in Task<TResult>; caso contrário, ele será considerado não especificado. - O tipo de conteúdo para o corpo de resposta especificado ou inferido é "application/json".
Em APIs mínimas, o Produces método de extensão e o [ProducesResponseType]
atributo definem apenas os metadados de resposta para o ponto de extremidade. Eles não modificam ou restringem o comportamento do ponto de extremidade, que pode retornar um código de status ou tipo de corpo de resposta diferente do especificado pelos metadados, e o tipo de conteúdo é determinado pelo tipo de retorno do método do manipulador de rotas, independentemente de qualquer tipo de conteúdo especificado em atributos ou métodos de extensão.
O Produces método de extensão pode especificar o tipo de resposta de um ponto de extremidade, com um código de status padrão de 200 e um tipo de conteúdo padrão de application/json
. O exemplo a seguir ilustra isso:
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.Produces<IList<Todo>>();
O [ProducesResponseType]
pode ser usado para adicionar metadados de resposta a um ponto de extremidade. Observe que o atributo é aplicado ao método do manipulador de rotas, não à invocação do método para criar a rota, conforme mostrado no exemplo a seguir:
app.MapGet("/todos",
[ProducesResponseType<List<Todo>>(200)]
async (TodoDb db) => await db.Todos.ToListAsync());
Usar TypedResults na implementação do manipulador de rotas de um ponto de extremidade inclui automaticamente os metadados de tipo de resposta para o ponto de extremidade. Por exemplo, o código a seguir anota automaticamente o ponto de extremidade com uma resposta no código de status 200
com um tipo de conteúdo application/json
.
app.MapGet("/todos", async (TodoDb db) =>
{
var todos = await db.Todos.ToListAsync();
return TypedResults.Ok(todos);
});
Somente os tipos de retorno que implementam IEndpointMetadataProvider criam uma responses
entrada no documento OpenAPI. Veja a seguir uma lista parcial de alguns dos TypedResults métodos auxiliares que produzem uma responses
entrada:
Método auxiliar TypedResults | código de status |
---|---|
Ok() | 200 |
Criado() | 201 |
CreatedAtRoute() | 201 |
Aceito() | 202 |
AcceptedAtRoute() | 202 |
NoContent() | 204 |
BadRequest() | 400 |
ValidationProblem() | 400 |
NotFound() | 404 |
Conflito() | 409 |
Entidade não processável() | 422 |
Todos esses métodos, exceto NoContent
têm uma sobrecarga genérica que especifica o tipo do corpo da resposta.
Uma classe pode ser implementada para definir os metadados do ponto de extremidade e retorná-los do manipulador de rotas.
Definir respostas para ProblemDetails
Ao definir o tipo de resposta para pontos de extremidade que podem retornar uma resposta ProblemDetails, o seguinte pode ser usado para adicionar os metadados de resposta apropriados para o ponto de extremidade:
- ProducesProblem
- O método de extensão ProducesValidationProblem.
- TypedResults com um código de status no intervalo (400-499).
Para obter mais informações sobre como configurar um aplicativo de API mínima para retornar respostas ProblemDetails, consulte Lidar com erros em APIs mínimas.
Vários tipos de resposta
Se um ponto de extremidade puder retornar diferentes tipos de resposta em cenários diferentes, você poderá fornecer metadados das seguintes maneiras:
Chame o método de extensão Produces várias vezes, conforme mostrado no exemplo a seguir:
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);
Use Results<TResult1,TResult2,TResult3,TResult4,TResult5,TResult6> na assinatura e TypedResults no corpo do manipulador, conforme mostrado no exemplo a seguir:
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(); });
Os
Results<TResult1,TResult2,TResultN>
(tipos de união) declaram que um manipulador de rotas retorna vários tipos concretos de implementaçãoIResult
, e qualquer um desses tipos que implementam IEndpointMetadataProvider contribuirá para os metadados do ponto de extremidade.Os tipos de união implementam operadores de conversão implícitos. Esses operadores permitem que o compilador converta automaticamente os tipos especificados nos argumentos genéricos em uma instância do tipo de união. Essa funcionalidade tem o benefício adicional de verificar durante o tempo de compilação se um manipulador de rotas retorna apenas os resultados que ele declara retornar. Tentar retornar um tipo que não é declarado como um dos argumentos genéricos para
Results<TResult1,TResult2,TResultN>
resulta em um erro de compilação.
Excluindo pontos de extremidade do documento gerado
Por padrão, todos os pontos de extremidade definidos em um aplicativo são documentados no arquivo OpenAPI gerado, mas os pontos de extremidade podem ser excluídos do documento usando atributos ou métodos de extensão.
O mecanismo para especificar um ponto de extremidade que deve ser excluído depende do tipo de aplicativo que está sendo desenvolvido.
As APIs mínimas dão suporte a duas estratégias para excluir um determinado endpoint do documento OpenAPI:
- ExcludeFromDescription Método de extensão
- Atributo
[ExcludeFromDescription]
A amostra a seguir demonstra as diferentes estratégias para excluir um determinado ponto de extremidade do documento OpenAPI gerado.
app.MapGet("/extension-method", () => "Hello world!")
.ExcludeFromDescription();
app.MapGet("/attributes",
[ExcludeFromDescription]
() => "Hello world!");
Inclusão de metadados OpenAPI para tipos de dados
As classes ou registros C# usados em corpos de solicitação ou resposta são representados como esquemas no documento OpenAPI gerado. Por padrão, somente as propriedades públicas são representadas no esquema, mas também há JsonSerializerOptions para criar propriedades de esquema para campos.
Quando o PropertyNamingPolicy é definido como camelCase (esse é o padrão em aplicativos Web do ASP.NET), os nomes de propriedade em um esquema são a forma camelCase do nome da propriedade de classe ou registro.
O [JsonPropertyName]
pode ser usado em uma propriedade individual para especificar o nome da propriedade no esquema.
tipo e formato
A biblioteca de esquema JSON mapeia tipos C# padrão para OpenAPI type
e format
da seguinte maneira:
Tipo de C# | OpenAPI type |
OpenAPI format |
---|---|---|
INT | Número inteiro | int32 |
longo | Número inteiro | int64 |
short | Número inteiro | int16 |
byte | Número inteiro | uint8 |
float | número | FLOAT |
double | número | duplo |
decimal | número | duplo |
bool | boolean | |
string | string | |
char | string | char |
byte[] | string | byte |
DateTimeOffset | string | date-time |
DateOnly | string | date |
TimeOnly | string | time |
Uri | string | uri |
Guid | string | uuid |
object | omitido | |
dynamic | omitido |
Observe que os tipos objeto e dinâmico não têm nenhum tipo definido no OpenAPI porque podem conter dados de qualquer tipo, incluindo tipos primitivos como número inteiro ou cadeia de caracteres.
O type
e format
também podem ser definidos com um Transformador de Esquema. Por exemplo, você pode querer que o format
de tipos decimais sejam decimal
em vez de double
.
Uso de atributos para adicionar metadados
ASP.NET usa metadados de atributos em propriedades de classe ou registro para definir metadados nas propriedades correspondentes do esquema gerado.
A tabela a seguir resume os atributos do namespace System.ComponentModel
que fornecem metadados para o esquema gerado:
Atributo | Descrição |
---|---|
[Description] |
Define o description de uma propriedade no esquema. |
[Required] |
Marca uma propriedade como required no esquema. |
[DefaultValue] |
Define o valor default de uma propriedade no esquema. |
[Range] |
Define o valor minimum e maximum de um inteiro ou número. |
[MinLength] |
Define o minLength de uma cadeia de caracteres. |
[MaxLength] |
Define o maxLength de uma string. |
[RegularExpression] |
Define o pattern de uma cadeia de caracteres. |
Observe que, em aplicativos baseados em controlador, esses atributos adicionam filtros à operação para validar se os dados de entrada atendem às restrições. Nas APIs mínimas, esses atributos definem os metadados no esquema gerado, mas a validação deve ser executada explicitamente por meio de um filtro de ponto de extremidade, na lógica do manipulador de rotas ou por meio de um pacote de terceiros.
Outras fontes de metadados para esquemas gerados
obrigatório
As propriedades também podem ser marcadas como required
com o modificador necessário.
enum
Os tipos enumeração em C# são baseados em inteiros, mas podem ser representados como cadeias de caracteres em JSON com um [JsonConverter]
e um JsonStringEnumConverter. Quando um tipo enumeração é representado como uma cadeia de caracteres em JSON, o esquema gerado terá uma propriedade enum
com os valores de cadeia de caracteres da enumeração.
Um tipo enumeração sem um [JsonConverter]
será definido como type: integer
no esquema gerado.
Observação: o [AllowedValues]
não define os valores enum
de uma propriedade.
nullable
As propriedades definidas como um valor anulável ou tipo de referência têm nullable: true
no esquema gerado. Isso é consistente com o comportamento padrão do desserializador System.Text.Json, que aceita null
como um valor válido para uma propriedade que permite valor nulo.
additionalProperties
Os esquemas são gerados sem uma asserção additionalProperties
por padrão, o que implica o padrão de true
. Isso é consistente com o comportamento padrão do desserializador System.Text.Json, que ignora silenciosamente propriedades adicionais em um objeto JSON.
Se as propriedades adicionais de um esquema tiverem apenas valores de um tipo específico, defina a propriedade ou classe como um Dictionary<string, type>
. O tipo da chave para o dicionário deve ser string
. Isso gera um esquema com additionalProperties
, especificando o esquema para "tipo" como os tipos de valor necessários.
Metadados para tipos polimórficos
Use os atributos [JsonPolymorphic]
e [JsonDerivedType]
em uma classe pai para especificar o campo discriminador e os subtipos para um tipo polimórfico.
O [JsonDerivedType]
adiciona o campo discriminador ao esquema para cada subclasse, com uma enumeração especificando o valor discriminador específico para a subclasse. Esse atributo também modifica o construtor de cada classe derivada para definir o valor do discriminador.
Uma classe abstrata com um atributo [JsonPolymorphic]
tem um campo discriminator
no esquema, mas uma classe concreta com um atributo [JsonPolymorphic]
não tem um campo discriminator
. O OpenAPI requer que a propriedade discriminatória seja uma propriedade obrigatória no esquema, mas como a propriedade discriminatória não está definida na classe base concreta, o esquema não pode incluir um campo discriminator
.
Adicionar metadados com um transformador de esquema
Um transformador de esquema pode ser usado para substituir quaisquer metadados padrão ou incluir metadados adicionais, como valores example
, ao esquema gerado. Para obter mais informações, consulte Usar transformadores de esquema.
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 o nome do documento OpenAPI
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
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");
É 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.
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, as verificações de autorização podem ser aplicadas ao documento OpenAPI. No código a seguir, o acesso ao documento OpenAPI é limitado a quem possui 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 três 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 de esquema se aplicam a cada esquema no documento. Eles podem ser usados para modificar o esquema de corpos de solicitação ou resposta ou qualquer esquema aninhado.
Os transformadores podem ser registrados no documento chamando o metodo AddDocumentTransformer 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.
- Registrar um transformador de operação usando uma instância de IOpenApiOperationTransformer.
- Registrar um transformador de operação usando um IOpenApiOperationTransformer ativado por DI.
- Registrar um transformador de esquema usando um delegado.
- Registrar um transformador de esquema usando uma instância de IOpenApiSchemaTransformer.
- Registrar um transformador de esquema usando um IOpenApiSchemaTransformer ativado por DI.
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();
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.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();
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.
- O ApiDescriptionGroups associado 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.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();
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.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;
}
}
}
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.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>()
});
}
}
}
}
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.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();
Usar transformadores de esquema
Esquemas são os modelos de dados usados em corpos de solicitação e resposta em um documento OpenAPI. Os transformadores de esquema são úteis quando uma modificação:
- Deve ser feita a cada esquema no documento, ou
- Aplicada condicionalmente a determinados esquemas.
Os transformadores de esquema têm acesso a um objeto de contexto que contém:
- O nome do documento ao qual o esquema pertence.
- As informações de tipo JSON associadas ao esquema de destino.
- O IServiceProvider usado na geração de documentos.
Por exemplo, o transformador de esquema a seguir define o format
de tipos decimais como decimal
em vez de 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; }
}
Recursos adicionais
As APIs mínimas fornecem suporte interno para gerar informações sobre pontos de extremidade em um aplicativo por meio do pacote Microsoft.AspNetCore.OpenApi
. Expor a definição do OpenAPI gerada por meio de uma interface do usuário visual requer um pacote de terceiros. Para obter informações sobre o suporte para OpenAPI em APIs baseadas em controlador, consulte a versão .NET 9 deste artigo.
O código a seguir é gerado pelo modelo de API Web mínima do ASP.NET Core e usa o 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);
}
No código realçado anterior:
- O
Microsoft.AspNetCore.OpenApi
é explicado na próxima seção. - AddEndpointsApiExplorer: configura o aplicativo para usar a API Explorer para descobrir e descrever pontos de extremidade com anotações padrão. O
WithOpenApi
substitui as anotações padrão e correspondentes geradas pela API Explorer com aquelas produzidas no pacoteMicrosoft.AspNetCore.OpenApi
. - O
UseSwagger
adiciona o middleware Swagger. - “UseSwaggerUI” permite uma versão incorporada da ferramenta Swagger UI.
- WithName: o IEndpointNameMetadata no ponto de extremidade é usado para geração de links e é tratado como a ID da operação na especificação do OpenAPI do ponto de extremidade fornecida.
- O
WithOpenApi
é explicado posteriormente neste artigo.
Pacote NuGet Microsoft.AspNetCore.OpenApi
O ASP.NET Core fornece o pacote Microsoft.AspNetCore.OpenApi
para interagir com as especificações do OpenAPI para pontos de extremidade. O pacote atua como um link entre os modelos do OpenAPI definidos no pacote Microsoft.AspNetCore.OpenApi
e os pontos de extremidade definidos em APIs Mínimas. O pacote fornece uma API que examina parâmetros, respostas e metadados de um ponto de extremidade para construir um tipo de anotação do OpenAPI usado para descrever um ponto de extremidade.
O Microsoft.AspNetCore.OpenApi
é adicionado como um PackageReference a um arquivo de projeto:
<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>
Ao usar Swashbuckle.AspNetCore
com Microsoft.AspNetCore.OpenApi
, a versão 6.4.0 do Swashbuckle.AspNetCore
ou posterior deve ser usada. A versão 1.4.3 do Microsoft.OpenApi
ou posterior deve ser usada para aproveitar os construtores de cópia nas invocações do WithOpenApi
.
Adicionar anotações do OpenAPI a pontos de extremidade por meio do WithOpenApi
Chamar o WithOpenApi
no ponto de extremidade complementa os metadados do ponto de extremidade. Esses metadados podem ser:
- Consumidos em pacotes de terceiros, como Swashbuckle.AspNetCore.
- Exibidos na interface do Swagger, no YAML ou no JSON gerado para definir a API.
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();
Modificar a anotação do OpenAPI no WithOpenApi
O método WithOpenApi
aceita uma função que pode ser usada para modificar a anotação do OpenAPI. Por exemplo, no código a seguir, uma descrição é adicionada ao primeiro parâmetro do ponto de extremidade:
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;
});
Adicionar IDs de operação ao OpenAPI
As IDs de operação são usadas para identificar exclusivamente um determinado ponto de extremidade no OpenAPI. O método de extensão WithName
pode ser usado para definir a ID da operação usada para um método .
app.MapGet("/todoitems2", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithName("GetToDoItems");
Como alternativa, a propriedade OperationId
pode ser definida diretamente na anotação do OpenAPI.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
OperationId = "GetTodos"
});
Adicionar marcas à descrição do OpenAPI
O OpenAPI dá suporte ao uso de objetos de marca para categorizar operações. Normalmente, essas marcas são usadas para agrupar operações na interface do usuário do Swagger. Essas marcas podem ser adicionadas a uma operação invocando o método de extensão WithTags no ponto de extremidade com as marcas desejadas.
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithTags("TodoGroup");
Como alternativa, a lista de OpenApiTags
pode ser definida na anotação do OpenAPI por meio do método de extensão WithOpenApi
.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
Tags = new List<OpenApiTag> { new() { Name = "Todos" } }
});
Adicionar resumo ou descrição do ponto de extremidade
O resumo e a descrição do ponto de extremidade podem ser adicionados invocando o método de extensão WithOpenApi
. No código a seguir, os resumos são definidos diretamente na anotação do OpenAPI.
app.MapGet("/todoitems2", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
Summary = "This is a summary",
Description = "This is a description"
});
Excluir descrição do OpenAPI
No exemplo a seguir, o ponto de extremidade /skipme
é excluído da geração de uma descrição do 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();
app.MapGet("/swag", () => "Hello Swagger!")
.WithOpenApi();
app.MapGet("/skipme", () => "Skipping Swagger.")
.ExcludeFromDescription();
app.Run();
Marcar uma API como obsoleta
Para marcar um ponto de extremidade como obsoleto, defina a propriedade Deprecated
na anotação do OpenAPI.
app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.WithOpenApi(operation => new(operation)
{
Deprecated = true
});
Descrever os tipos de resposta
O OpenAPI dá suporte ao fornecimento de uma descrição das respostas retornadas de uma API. As APIs mínimas dão suporte a três estratégias para definir o tipo de resposta de um ponto de extremidade:
- Por meio do método de extensão
Produces
no ponto de extremidade - Por meio do atributo
ProducesResponseType
no manipulador de rotas - Retornando
TypedResults
do manipulador de rotas
O método de extensão Produces
pode ser usado para adicionar metadados Produces
a um ponto de extremidade. Quando nenhum parâmetro é fornecido, o método de extensão preenche metadados para o tipo de destino em um código de status 200
e um tipo de conteúdo application/json
.
app
.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync())
.Produces<IList<Todo>>();
Usar TypedResults
na implementação do manipulador de rotas de um ponto de extremidade inclui automaticamente os metadados de tipo de resposta para o ponto de extremidade. Por exemplo, o código a seguir anota automaticamente o ponto de extremidade com uma resposta no código de status 200
com um tipo de conteúdo application/json
.
app.MapGet("/todos", async (TodoDb db) =>
{
var todos = await db.Todos.ToListAsync());
return TypedResults.Ok(todos);
});
Definir respostas para ProblemDetails
Ao definir o tipo de resposta para pontos de extremidade que podem retornar uma resposta ProblemDetails, o método de extensão ProducesProblem, ProducesValidationProblem ou TypedResults.Problem
pode ser usado para adicionar a anotação apropriada aos metadados do ponto de extremidade. Observe que os métodos de extensão ProducesProblem
e ProducesValidationProblem
não podem ser usados com grupos de rotas no .NET 8 e anteriores.
Quando não há anotações explícitas fornecidas por uma das estratégias acima, a estrutura tenta determinar um tipo de resposta padrão examinando a assinatura da resposta. Essa resposta padrão é preenchida sob o código de status 200
na definição do OpenAPI.
Vários tipos de resposta
Se um ponto de extremidade puder retornar diferentes tipos de resposta em cenários diferentes, você poderá fornecer metadados das seguintes maneiras:
Chame o método de extensão
Produces
várias vezes, conforme mostrado no exemplo a seguir: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);
Use
Results<TResult1,TResult2,TResultN>
na assinatura eTypedResults
no corpo do manipulador, conforme mostrado no exemplo a seguir: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(); });
Os
Results<TResult1,TResult2,TResultN>
(tipos de união) declaram que um manipulador de rotas retorna vários tipos concretos de implementaçãoIResult
, e qualquer um desses tipos que implementamIEndpointMetadataProvider
contribuirá para os metadados do ponto de extremidade.Os tipos de união implementam operadores de conversão implícitos. Esses operadores permitem que o compilador converta automaticamente os tipos especificados nos argumentos genéricos em uma instância do tipo de união. Essa funcionalidade tem o benefício adicional de verificar durante o tempo de compilação se um manipulador de rotas retorna apenas os resultados que ele declara retornar. Tentar retornar um tipo que não é declarado como um dos argumentos genéricos para
Results<TResult1,TResult2,TResultN>
resulta em um erro de compilação.
Descrever o corpo da solicitação e os parâmetros
Além de descrever os tipos retornados por um ponto de extremidade, o OpenAPI também dá suporte à anotação das entradas consumidas por uma API. Essas entradas se enquadram em duas categorias:
- Parâmetros que aparecem no caminho, cadeia de caracteres de consulta, cabeçalhos ou cookies
- Dados transmitidos como parte do corpo da solicitação
A estrutura infere os tipos de parâmetros de solicitação no caminho, na consulta e na cadeia de caracteres de cabeçalho automaticamente com base na assinatura do manipulador de rotas.
Para definir o tipo de entradas transmitidas como o corpo da solicitação, configure as propriedades usando o método de extensão Accepts
para definir o tipo de objeto e o tipo de conteúdo esperados pelo manipulador de solicitação. No exemplo a seguir, o ponto de extremidade aceita um objeto Todo
no corpo da solicitação com um tipo de conteúdo esperado de application/xml
.
app.MapPost("/todos/{id}", (int id, Todo todo) => ...)
.Accepts<Todo>("application/xml");
Além do método de extensão Accepts
, um tipo de parâmetro pode descrever sua própria anotação implementando a interface IEndpointParameterMetadataProvider
. Por exemplo, o tipo Todo
a seguir adiciona uma anotação que requer um corpo da solicitação com um tipo de conteúdo application/xml
.
public class Todo : IEndpointParameterMetadataProvider
{
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
{
builder.Metadata.Add(new ConsumesAttribute(typeof(Todo), isOptional: false, "application/xml"));
}
}
Quando nenhuma anotação explícita é fornecida, a estrutura tenta determinar o tipo de solicitação padrão se houver um parâmetro do corpo da solicitação no manipulador de ponto de extremidade. A inferência usa a seguinte heurística para produzir a anotação:
- Os parâmetros do corpo da solicitação lidos de um formulário por meio do atributo
[FromForm]
são descritos com o tipo de conteúdomultipart/form-data
. - Todos os outros parâmetros do corpo da solicitação são descritos com o tipo de conteúdo
application/json
. - O corpo da solicitação será tratado como opcional se for anulável ou se a propriedade
AllowEmpty
estiver definida no atributoFromBody
.
Suporte ao controle de versão da API
As APIs mínimas dão suporte ao controle de versão da API por meio do pacote Asp.Versioning.Http. Exemplos de configuração de controle de versão com APIs mínimas podem ser encontrados no repositório de controle de versão da API.
Código-fonte do ASP.NET Core OpenAPI no GitHub
Recursos adicionais
Um aplicativo de API mínima pode descrever a especificação do OpenAPI para manipuladores de rota usando o Swashbuckle.
Para obter informações sobre o suporte para OpenAPI em APIs baseadas em controlador, consulte a versão .NET 9 deste artigo.
O código a seguir é um aplicativo do ASP.NET Core típico com suporte ao OpenAPI:
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();
Excluir descrição do OpenAPI
No exemplo a seguir, o ponto de extremidade /skipme
é excluído da geração de uma descrição do OpenAPI:
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();
Descrever os tipos de resposta
O exemplo a seguir usa os tipos de resultados internos para personalizar a resposta:
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);
Adicionar IDs de operação ao OpenAPI
app.MapGet("/todoitems2", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithName("GetToDoItems");
Adicionar marcas à descrição do OpenAPI
O código a seguir usa uma marca de agrupamento do OpenAPI:
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync())
.WithTags("TodoGroup");