Suporte ao OpenAPI em aplicativos de APIs mínimas
A especificação do OpenAPI é um padrão independente de linguagem de programação para documentar APIs HTTP. Esse padrão tem suporte em APIs mínimas por meio de uma combinação de APIs internas e bibliotecas de software livre. Há três aspectos principais para a integração do OpenAPI em um aplicativo:
- Gerando informações sobre os pontos de extremidade no aplicativo.
- Coletando as informações em um formato que corresponde ao esquema do OpenAPI.
- Expondo o esquema do OpenAPI gerado por meio de uma interface do usuário visual ou um arquivo serializado.
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.
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. Requer o pacote nugetSwashbuckle.AspNetCore
. - O
UseSwaggerUI
habilita uma versão inserida da ferramenta de interface do usuário do Swagger no modo de desenvolvimento. - 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 usuário 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
ou TypedResults.Problem
pode ser usado para adicionar a anotação apropriada aos metadados do ponto de extremidade.
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
A especificação do OpenAPI é um padrão independente de linguagem de programação para documentar APIs HTTP. Esse padrão tem suporte em APIs mínimas por meio de uma combinação de APIs internas e bibliotecas de software livre. Há três aspectos principais para a integração do OpenAPI em um aplicativo:
- Gerando informações sobre os pontos de extremidade no aplicativo.
- Coletando as informações em um formato que corresponde ao esquema do OpenAPI.
- Expondo o esquema do OpenAPI gerado por meio de uma interface do usuário visual ou um arquivo serializado.
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.
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 usuário 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
ou TypedResults.Problem
pode ser usado para adicionar a anotação apropriada aos metadados do ponto de extremidade.
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 pode descrever a especificação do OpenAPI para manipuladores de rota usando o Swashbuckle.
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");
Comentários
https://aka.ms/ContentUserFeedback.
Em breve: Ao longo de 2024, eliminaremos os problemas do GitHub como o mecanismo de comentários para conteúdo e o substituiremos por um novo sistema de comentários. Para obter mais informações, consulteEnviar e exibir comentários de