Partilhar via


Criar APIs Web com o ASP.NET Core

O ASP.NET Core dá suporte à criação de APIs Web usando controladores ou APIs mínimas. Em uma API Web, os controladores são classes que derivam de ControllerBase. Os controladores são ativados e descartados por solicitação.

Este artigo mostra como usar controladores para lidar com solicitações da API Web. Para obter informações sobre como criar APIs Web sem controladores, consulte Tutorial: Criar uma API mínima com ASP.NET Core.

Classe ControllerBase

Uma API Web baseada em controlador consiste em uma ou mais classes de controlador que derivam de ControllerBase. O modelo de projeto da API Web fornece um controlador inicial:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Os controladores de API Web normalmente devem derivar de ControllerBase, não de Controller. Controller é derivado de ControllerBase e agrega suporte para exibições; portanto, serve para manipulação de páginas da Web, não para solicitações de API Web. Se o mesmo controlador precisar dar suporte a exibições e APIs Web, deriva de Controller.

A classe ControllerBase fornece muitas propriedades e métodos úteis para lidar com solicitações HTTP. Por exemplo, CreatedAtAction retorna um código de status 201:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

A tabela a seguir contém exemplos de métodos em ControllerBase.

Método Observações
BadRequest Retorna o código de status 400.
NotFound Retorna o código de status 404.
PhysicalFile Retorna um arquivo.
TryUpdateModelAsync Invoca model binding.
TryValidateModel Invoca validação de modelo.

Confira uma lista com todos os métodos e propriedades disponíveis em ControllerBase.

Atributos

O namespace Microsoft.AspNetCore.Mvc fornece atributos que podem ser usados para configurar o comportamento de controladores de API Web e dos métodos de ação. O exemplo a seguir usa atributos para especificar o verbo de ação HTTP compatível e quaisquer códigos de status HTTP conhecidos que possam ser retornados:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Confira mais alguns exemplos de atributos disponíveis.

Atributo Observações
[Route] Especifica o padrão de URL para um controlador ou ação.
[Bind] Especifica o prefixo e as propriedades que serão incluídos no model binding.
[HttpGet] Identifica uma ação que dá suporte ao verbo de ação HTTP GET.
[Consumes] Especifica os tipos de dados aceitos por uma ação.
[Produces] Especifica os tipos de dados retornados por uma ação.

Veja uma lista que inclui os atributos disponíveis no namespace Microsoft.AspNetCore.Mvc.

Atributo ApiController

O atributo [ApiController] pode ser aplicado a uma classe de controlador para permitir os seguintes comportamentos opinativos específicos da API:

Atributo em controladores específicos

O atributo [ApiController] pode ser aplicado a controladores específicos, como no exemplo a seguir do modelo de projeto:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Atributo em vários controladores

Uma abordagem ao uso do atributo em mais de um controlador é a criação de uma classe de controlador base personalizada anotada com o atributo [ApiController]. O exemplo a seguir mostra uma classe base personalizada e um controlador derivado dela:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Atributo em um assembly

O atributo [ApiController] pode ser aplicado a um assembly. Quando o atributo [ApiController] é aplicado a um assembly, todos os controladores no assembly têm o atributo [ApiController] aplicado. Não é possível recusar controladores individuais. Aplique o atributo de nível de assembly ao arquivo Program.cs:

using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Requisito de roteamento de atributo

O atributo [ApiController] transforma em requisito o roteamento de atributo. Por exemplo:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

As ações são inacessíveis por meio de rotas convencionais definidas por UseEndpoints, UseMvc ou UseMvcWithDefaultRoute.

Respostas HTTP 400 automáticas

O atributo [ApiController] faz com que os erros de validação do modelo disparem automaticamente uma resposta HTTP 400. Consequentemente, o código a seguir se torna desnecessário em um método de ação:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

O MVC do ASP.NET Core usa o filtro de ação ModelStateInvalidFilter para fazer a verificação anterior.

Resposta BadRequest padrão

O tipo de resposta padrão para uma resposta HTTP 400 é ValidationProblemDetails. O corpo da resposta a seguir é um exemplo do tipo serializado:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

O tipo ValidationProblemDetails:

  • Fornece um formato legível por computador para especificar erros nas respostas da API Web.
  • Em conformidade com a especificação RFC 7807.

Para tornar as respostas automáticas e personalizadas consistentes, chame o método ValidationProblem em vez de BadRequest. ValidationProblem retorna um objeto ValidationProblemDetails, bem como a resposta automática.

Registrar respostas de 400 automática

Para registrar 400 respostas automáticas, defina a propriedade delegada InvalidModelStateResponseFactory para executar o processamento personalizado. Por padrão, InvalidModelStateResponseFactory usa ProblemDetailsFactory para criar uma instância de ValidationProblemDetails.

O exemplo a seguir mostra como recuperar uma instância de ILogger<TCategoryName> para registrar informações sobre uma resposta 400 automática:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices
                                .GetRequiredService<ILogger<Program>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails
            // response.
            // To produce a custom response, return a different implementation of 
            // IActionResult instead.
            return builtInFactory(context);
        };
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Desabilitar resposta de 400 automática

Para desabilitar o comportamento 400 automático, defina a propriedade SuppressModelStateInvalidFilter como true. Adicione o seguinte código realçado:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inferência de parâmetro de origem da associação

Um atributo de origem de associação define o local no qual o valor do parâmetro de uma ação é encontrado. Os seguintes atributos da origem da associação existem:

Atributo Origem de associação
[FromBody] Corpo da solicitação
[FromForm] Dados do formulário no corpo da solicitação
[FromHeader] Cabeçalho da solicitação
[FromQuery] Parâmetro de cadeia de caracteres de consulta de solicitação
[FromRoute] Dados de rota da solicitação atual
[FromServices] O serviço de solicitação inserido como um parâmetro de ação
[AsParameters] Parâmetros de método

Aviso

Não use [FromRoute] quando os valores puderem conter %2f (ou seja, /). %2f não ficará sem escape para /. Use [FromQuery], se o valor puder conter %2f.

Sem o atributo [ApiController] ou outros atributos de origem da associação, como [FromQuery], o runtime do ASP.NET Core tenta usar o associador de modelos de objeto complexo. O associador de modelos de objeto complexo extrai os dados dos provedores de valor em uma ordem definida.

No exemplo a seguir, o atributo [FromQuery] indica que o valor do parâmetro discontinuedOnly é fornecido na cadeia de caracteres de consulta da URL de solicitação:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

O atributo [ApiController] aplica regras de inferência para as fontes de dados padrão dos parâmetros de ação. Essas regras poupam você da necessidade de identificar as origens de associação manualmente aplicando atributos aos parâmetros de ação. As regras de inferência da origem de associação se comportam da seguinte maneira:

  • [FromServices] é inferido para parâmetros de tipo complexos registrados no Contêiner de DI.
  • [FromBody] é inferido para parâmetros de tipo complexos não registrados no Contêiner de DI. Uma exceção à regra de inferência [FromBody] é qualquer tipo interno complexo com um significado especial, como IFormCollection e CancellationToken. O código de inferência da origem da associação ignora esses tipos especiais.
  • [FromForm] é inferido para parâmetros de ação do tipo IFormFile e IFormFileCollection. Ele não é inferido para qualquer tipo simples ou definido pelo usuário.
  • [FromRoute] é inferido para qualquer nome de parâmetro de ação correspondente a um parâmetro no modelo de rota. Quando mais de uma rota correspondem a um parâmetro de ação, qualquer valor de rota é considerado [FromRoute].
  • [FromQuery] é inferido para todos os outros parâmetros de ação.

Notas de inferência FromBody

[FromBody] não é inferido para tipos simples, como string ou int. Portanto, o atributo [FromBody] deve ser usado para tipos simples quando essa funcionalidade for necessária.

Quando uma ação tiver mais de um parâmetro associado ao corpo da solicitação, uma exceção será lançada. Por exemplo, todas as assinaturas de método de ação a seguir causam uma exceção:

  • [FromBody] inferido em ambos, pois são tipos complexos.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • O atributo [FromBody] em um, inferido no outro, porque é um tipo complexo.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Atributo [FromBody] em ambos.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Observações de inferência do FromServices

A associação de parâmetros vincula parâmetros por meio de injeção de dependência quando o tipo é configurado como um serviço. Isso significa que não é necessário aplicar explicitamente o atributo [FromServices] a um parâmetro. No código a seguir, ambas as ações retornam a hora:

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    public ActionResult GetWithAttribute([FromServices] IDateTime dateTime) 
                                                        => Ok(dateTime.Now);

    [Route("noAttribute")]
    public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now);
}

Em casos raros, a DI automática pode interromper aplicativos que têm um tipo de DI que também é aceito nos métodos de ação de um controlador de API. Não é comum ter um tipo na DI e como argumento em uma ação do controlador da API.

Para desabilitar a inferência [FromServices] para um único parâmetro de ação, aplique o atributo de origem de associação desejado ao parâmetro. Por exemplo, aplique o atributo [FromBody] a um parâmetro de ação que deve ser vinculado ao corpo da solicitação.

Para desabilitar a inferência [FromServices] globalmente, defina DisableImplicitFromServicesParameters como true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IDateTime, SystemDateTime>();

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

var app = builder.Build();

app.MapControllers();

app.Run();

Os tipos são verificados na inicialização do aplicativo com IServiceProviderIsService para determinar se um argumento em uma ação do controlador de API vem da DI ou de outras fontes.

O mecanismo para inferir a origem da vinculação dos parâmetros de ação do API Controller usa as seguintes regras:

Desabilitar regras de inferência

Para desabilitar a inferência da origem da associação, defina SuppressInferBindingSourcesForParameters como true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inferência de solicitação de várias partes/dados de formulário

O atributo [ApiController] aplica uma regra de inferência para parâmetros de ação do tipo IFormFile e IFormFileCollection. O tipo de conteúdo da solicitação multipart/form-data é inferido para esses tipos.

Para desabilitar o comportamento padrão, defina a propriedade SuppressConsumesConstraintForFormFileParameters como true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Detalhes do problema dos códigos de status de erro

O MVC transforma um resultado de erro (um resultado com o código de status 400 ou superior) em um resultado com ProblemDetails. O tipo ProblemDetails tem base na especificação RFC 7807 para fornecer detalhes de erro legíveis por computador em uma resposta HTTP.

Considere o seguinte código em uma ação do controlador:

if (pet == null)
{
    return NotFound();
}

O método NotFound produz um código de status HTTP 404 com um corpo ProblemDetails. Por exemplo:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Desabilitar a resposta de ProblemDetails

A criação automática de um ProblemDetails para códigos de status de erro fica desabilitada quando a propriedade SuppressMapClientErrors é definida como true. Adicione o seguinte código:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Definir os tipos de conteúdo de solicitação com suporte com o atributo [Consome]

Por padrão, uma ação dá suporte a todos os tipos de conteúdo de solicitação disponíveis. Por exemplo, se um aplicativo estiver configurado para dar suporte aos formatadores de entrada de JSON e XML, uma ação dará suporte a vários tipos de conteúdo, incluindo application/json e application/xml.

O atributo [Consome] permite que uma ação limite os tipos de conteúdo de solicitação com suporte. Aplique o atributo [Consumes] a uma ação ou controlador, especificando um ou mais tipos de conteúdo:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

No código anterior, a ação CreateProduct especifica o tipo de conteúdo application/xml. As solicitações roteadas para essa ação devem especificar um cabeçalho Content-Type de application/xml. Solicitações que não especificam um cabeçalho Content-Type de application/xml resultam em uma resposta Tipo de Mídia Sem Suporte 415.

O atributo [Consumes] também permite que uma ação influencie sua seleção com base no tipo de conteúdo de uma solicitação de entrada, aplicando uma restrição de tipo. Considere o seguinte exemplo:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

No código anterior, ConsumesController está configurado para lidar com solicitações enviadas para a URL https://localhost:5001/api/Consumes. Ambas as ações do controlador, PostJson e PostForm, tratam de solicitações POST com a mesma URL. Sem o atributo [Consumes] que aplica uma restrição de tipo, uma exceção de correspondência ambígua é lançada.

O atributo [Consumes] é aplicado a ambas as ações. A ação PostJson trata as solicitações enviadas com um cabeçalho Content-Type de application/json. A ação PostForm trata as solicitações enviadas com um cabeçalho Content-Type de application/x-www-form-urlencoded.

Recursos adicionais

O ASP.NET Core dá suporte à criação de APIs Web usando controladores ou APIs mínimas. Em uma API Web, os controladores são classes que derivam de ControllerBase. Este artigo mostra como usar controladores para lidar com solicitações da API Web. Para obter informações sobre como criar APIs Web sem controladores, consulte Tutorial: Criar uma API mínima com ASP.NET Core.

Classe ControllerBase

Uma API Web baseada em controlador consiste em uma ou mais classes de controlador que derivam de ControllerBase. O modelo de projeto da API Web fornece um controlador inicial:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Os controladores de API Web normalmente devem derivar de ControllerBase, não de Controller. Controller é derivado de ControllerBase e agrega suporte para exibições; portanto, serve para manipulação de páginas da Web, não para solicitações de API Web. Se o mesmo controlador precisar dar suporte a exibições e APIs Web, deriva de Controller.

A classe ControllerBase fornece muitas propriedades e métodos úteis para lidar com solicitações HTTP. Por exemplo, CreatedAtAction retorna um código de status 201:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

A tabela a seguir contém exemplos de métodos em ControllerBase.

Método Observações
BadRequest Retorna o código de status 400.
NotFound Retorna o código de status 404.
PhysicalFile Retorna um arquivo.
TryUpdateModelAsync Invoca model binding.
TryValidateModel Invoca validação de modelo.

Confira uma lista com todos os métodos e propriedades disponíveis em ControllerBase.

Atributos

O namespace Microsoft.AspNetCore.Mvc fornece atributos que podem ser usados para configurar o comportamento de controladores de API Web e dos métodos de ação. O exemplo a seguir usa atributos para especificar o verbo de ação HTTP compatível e quaisquer códigos de status HTTP conhecidos que possam ser retornados:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Confira mais alguns exemplos de atributos disponíveis.

Atributo Observações
[Route] Especifica o padrão de URL para um controlador ou ação.
[Bind] Especifica o prefixo e as propriedades que serão incluídos no model binding.
[HttpGet] Identifica uma ação que dá suporte ao verbo de ação HTTP GET.
[Consumes] Especifica os tipos de dados aceitos por uma ação.
[Produces] Especifica os tipos de dados retornados por uma ação.

Veja uma lista que inclui os atributos disponíveis no namespace Microsoft.AspNetCore.Mvc.

Atributo ApiController

O atributo [ApiController] pode ser aplicado a uma classe de controlador para permitir os seguintes comportamentos opinativos específicos da API:

Atributo em controladores específicos

O atributo [ApiController] pode ser aplicado a controladores específicos, como no exemplo a seguir do modelo de projeto:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Atributo em vários controladores

Uma abordagem ao uso do atributo em mais de um controlador é a criação de uma classe de controlador base personalizada anotada com o atributo [ApiController]. O exemplo a seguir mostra uma classe base personalizada e um controlador derivado dela:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Atributo em um assembly

O atributo [ApiController] pode ser aplicado a um assembly. Quando o atributo [ApiController] é aplicado a um assembly, todos os controladores no assembly têm o atributo [ApiController] aplicado. Não é possível recusar controladores individuais. Aplique o atributo de nível de assembly ao arquivo Program.cs:

using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Requisito de roteamento de atributo

O atributo [ApiController] transforma em requisito o roteamento de atributo. Por exemplo:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

As ações são inacessíveis por meio de rotas convencionais definidas por UseEndpoints, UseMvc ou UseMvcWithDefaultRoute.

Respostas HTTP 400 automáticas

O atributo [ApiController] faz com que os erros de validação do modelo disparem automaticamente uma resposta HTTP 400. Consequentemente, o código a seguir se torna desnecessário em um método de ação:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

O MVC do ASP.NET Core usa o filtro de ação ModelStateInvalidFilter para fazer a verificação anterior.

Resposta BadRequest padrão

O corpo da resposta a seguir é um exemplo do tipo serializado:

{
  "": [
    "A non-empty request body is required."
  ]
}

O tipo de resposta padrão para uma resposta HTTP 400 é ValidationProblemDetails. O corpo da resposta a seguir é um exemplo do tipo serializado:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

O tipo ValidationProblemDetails:

  • Fornece um formato legível por computador para especificar erros nas respostas da API Web.
  • Em conformidade com a especificação RFC 7807.

Para tornar as respostas automáticas e personalizadas consistentes, chame o método ValidationProblem em vez de BadRequest. ValidationProblem retorna um objeto ValidationProblemDetails, bem como a resposta automática.

Registrar respostas de 400 automática

Para registrar 400 respostas automáticas, defina a propriedade delegada InvalidModelStateResponseFactory para executar o processamento personalizado. Por padrão, InvalidModelStateResponseFactory usa ProblemDetailsFactory para criar uma instância de ValidationProblemDetails.

O exemplo a seguir mostra como recuperar uma instância de ILogger<TCategoryName> para registrar informações sobre uma resposta 400 automática:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices
                                .GetRequiredService<ILogger<Program>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails
            // response.
            // To produce a custom response, return a different implementation of 
            // IActionResult instead.
            return builtInFactory(context);
        };
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Desabilitar resposta de 400 automática

Para desabilitar o comportamento 400 automático, defina a propriedade SuppressModelStateInvalidFilter como true. Adicione o seguinte código realçado:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inferência de parâmetro de origem da associação

Um atributo de origem de associação define o local no qual o valor do parâmetro de uma ação é encontrado. Os seguintes atributos da origem da associação existem:

Atributo Origem de associação
[FromBody] Corpo da solicitação
[FromForm] Dados do formulário no corpo da solicitação
[FromHeader] Cabeçalho da solicitação
[FromQuery] Parâmetro de cadeia de caracteres de consulta de solicitação
[FromRoute] Dados de rota da solicitação atual
[FromServices] O serviço de solicitação inserido como um parâmetro de ação

Aviso

Não use [FromRoute] quando os valores puderem conter %2f (ou seja, /). %2f não ficará sem escape para /. Use [FromQuery], se o valor puder conter %2f.

Sem o atributo [ApiController] ou outros atributos de origem da associação, como [FromQuery], o runtime do ASP.NET Core tenta usar o associador de modelos de objeto complexo. O associador de modelos de objeto complexo extrai os dados dos provedores de valor em uma ordem definida.

No exemplo a seguir, o atributo [FromQuery] indica que o valor do parâmetro discontinuedOnly é fornecido na cadeia de caracteres de consulta da URL de solicitação:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

O atributo [ApiController] aplica regras de inferência para as fontes de dados padrão dos parâmetros de ação. Essas regras poupam você da necessidade de identificar as origens de associação manualmente aplicando atributos aos parâmetros de ação. As regras de inferência da origem de associação se comportam da seguinte maneira:

  • [FromBody] é inferido para parâmetros de tipo complexos não registrados no Contêiner de DI. Uma exceção à regra de inferência [FromBody] é qualquer tipo interno complexo com um significado especial, como IFormCollection e CancellationToken. O código de inferência da origem da associação ignora esses tipos especiais.
  • [FromForm] é inferido para parâmetros de ação do tipo IFormFile e IFormFileCollection. Ele não é inferido para qualquer tipo simples ou definido pelo usuário.
  • [FromRoute] é inferido para qualquer nome de parâmetro de ação correspondente a um parâmetro no modelo de rota. Quando mais de uma rota correspondem a um parâmetro de ação, qualquer valor de rota é considerado [FromRoute].
  • [FromQuery] é inferido para todos os outros parâmetros de ação.

Notas de inferência FromBody

[FromBody] não é inferido para tipos simples, como string ou int. Portanto, o atributo [FromBody] deve ser usado para tipos simples quando essa funcionalidade for necessária.

Quando uma ação tiver mais de um parâmetro associado ao corpo da solicitação, uma exceção será lançada. Por exemplo, todas as assinaturas de método de ação a seguir causam uma exceção:

  • [FromBody] inferido em ambos, pois são tipos complexos.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • O atributo [FromBody] em um, inferido no outro, porque é um tipo complexo.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Atributo [FromBody] em ambos.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Desabilitar regras de inferência

Para desabilitar a inferência da origem da associação, defina SuppressInferBindingSourcesForParameters como true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Inferência de solicitação de várias partes/dados de formulário

O atributo [ApiController] aplica uma regra de inferência para parâmetros de ação do tipo IFormFile e IFormFileCollection. O tipo de conteúdo da solicitação multipart/form-data é inferido para esses tipos.

Para desabilitar o comportamento padrão, defina a propriedade SuppressConsumesConstraintForFormFileParameters como true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Detalhes do problema dos códigos de status de erro

O MVC transforma um resultado de erro (um resultado com o código de status 400 ou superior) em um resultado com ProblemDetails. O tipo ProblemDetails tem base na especificação RFC 7807 para fornecer detalhes de erro legíveis por computador em uma resposta HTTP.

Considere o seguinte código em uma ação do controlador:

if (pet == null)
{
    return NotFound();
}

O método NotFound produz um código de status HTTP 404 com um corpo ProblemDetails. Por exemplo:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Desabilitar a resposta de ProblemDetails

A criação automática de um ProblemDetails para códigos de status de erro fica desabilitada quando a propriedade SuppressMapClientErrors é definida como true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Definir os tipos de conteúdo de solicitação com suporte com o atributo [Consome]

Por padrão, uma ação dá suporte a todos os tipos de conteúdo de solicitação disponíveis. Por exemplo, se um aplicativo estiver configurado para dar suporte aos formatadores de entrada de JSON e XML, uma ação dará suporte a vários tipos de conteúdo, incluindo application/json e application/xml.

O atributo [Consome] permite que uma ação limite os tipos de conteúdo de solicitação com suporte. Aplique o atributo [Consumes] a uma ação ou controlador, especificando um ou mais tipos de conteúdo:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

No código anterior, a ação CreateProduct especifica o tipo de conteúdo application/xml. As solicitações roteadas para essa ação devem especificar um cabeçalho Content-Type de application/xml. Solicitações que não especificam um cabeçalho Content-Type de application/xml resultam em uma resposta Tipo de Mídia Sem Suporte 415.

O atributo [Consumes] também permite que uma ação influencie sua seleção com base no tipo de conteúdo de uma solicitação de entrada, aplicando uma restrição de tipo. Considere o seguinte exemplo:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

No código anterior, ConsumesController está configurado para lidar com solicitações enviadas para a URL https://localhost:5001/api/Consumes. Ambas as ações do controlador, PostJson e PostForm, tratam de solicitações POST com a mesma URL. Sem o atributo [Consumes] que aplica uma restrição de tipo, uma exceção de correspondência ambígua é lançada.

O atributo [Consumes] é aplicado a ambas as ações. A ação PostJson trata as solicitações enviadas com um cabeçalho Content-Type de application/json. A ação PostForm trata as solicitações enviadas com um cabeçalho Content-Type de application/x-www-form-urlencoded.

Recursos adicionais

O ASP.NET Core dá suporte à criação de serviços RESTful, também conhecidos como APIs Web, usando C#. Para lidar com solicitações, uma API Web usa controladores. Em uma API Web, os controladores são classes que derivam de ControllerBase. Este artigo mostra como usar controladores para lidar com solicitações da API Web.

Exibir ou baixar o código de exemplo. (Como baixar.)

Classe ControllerBase

Uma API Web consiste em uma ou mais classes de controlador derivadas de ControllerBase. O modelo de projeto da API Web fornece um controlador inicial:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Não crie um controlador de API Web derivando da classe base Controller. Controller é derivado de ControllerBase e agrega suporte para exibições; portanto, serve para manipulação de páginas da Web, não para solicitações de API Web. Há uma exceção a essa regra: se você planeja usar o mesmo controlador para visualizações e APIs Web, deduza-o de Controller.

A classe ControllerBase fornece muitas propriedades e métodos úteis para lidar com solicitações HTTP. Por exemplo, ControllerBase.CreatedAtAction retorna um código de status 201:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Veja mais alguns exemplos de métodos fornecidos por ControllerBase.

Método Observações
BadRequest Retorna o código de status 400.
NotFound Retorna o código de status 404.
PhysicalFile Retorna um arquivo.
TryUpdateModelAsync Invoca model binding.
TryValidateModel Invoca validação de modelo.

Confira uma lista com todos os métodos e propriedades disponíveis em ControllerBase.

Atributos

O namespace Microsoft.AspNetCore.Mvc fornece atributos que podem ser usados para configurar o comportamento de controladores de API Web e dos métodos de ação. O exemplo a seguir usa atributos para especificar o verbo de ação HTTP compatível e quaisquer códigos de status HTTP conhecidos que possam ser retornados:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Confira mais alguns exemplos de atributos disponíveis.

Atributo Observações
[Route] Especifica o padrão de URL para um controlador ou ação.
[Bind] Especifica o prefixo e as propriedades que serão incluídos no model binding.
[HttpGet] Identifica uma ação que dá suporte ao verbo de ação HTTP GET.
[Consumes] Especifica os tipos de dados aceitos por uma ação.
[Produces] Especifica os tipos de dados retornados por uma ação.

Veja uma lista que inclui os atributos disponíveis no namespace Microsoft.AspNetCore.Mvc.

Atributo ApiController

O atributo [ApiController] pode ser aplicado a uma classe de controlador para permitir os seguintes comportamentos opinativos específicos da API:

Atributo em controladores específicos

O atributo [ApiController] pode ser aplicado a controladores específicos, como no exemplo a seguir do modelo de projeto:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Atributo em vários controladores

Uma abordagem ao uso do atributo em mais de um controlador é a criação de uma classe de controlador base personalizada anotada com o atributo [ApiController]. O exemplo a seguir mostra uma classe base personalizada e um controlador derivado dela:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Atributo em um assembly

O atributo [ApiController] pode ser aplicado a um assembly. A anotação dessa maneira aplica o comportamento da API Web para todos os controladores no assembly. Não é possível recusar controladores individuais. Aplique o atributo de nível de assembly à declaração de namespace em torno da classe Startup:

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

Requisito de roteamento de atributo

O atributo [ApiController] transforma em requisito o roteamento de atributo. Por exemplo:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

As ações são inacessíveis por meio de rotas convencionais definidas por UseEndpoints, UseMvc, ou UseMvcWithDefaultRoute em Startup.Configure.

Respostas HTTP 400 automáticas

O atributo [ApiController] faz com que os erros de validação do modelo disparem automaticamente uma resposta HTTP 400. Consequentemente, o código a seguir se torna desnecessário em um método de ação:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

O MVC do ASP.NET Core usa o filtro de ação ModelStateInvalidFilter para fazer a verificação anterior.

Resposta BadRequest padrão

O corpo da solicitação a seguir é um exemplo do tipo serializado:

{
  "": [
    "A non-empty request body is required."
  ]
}

O tipo de resposta padrão para uma resposta HTTP 400 é ValidationProblemDetails. O corpo da solicitação a seguir é um exemplo do tipo serializado:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

O tipo ValidationProblemDetails:

  • Fornece um formato legível por computador para especificar erros nas respostas da API Web.
  • Em conformidade com a especificação RFC 7807.

Para tornar as respostas automáticas e personalizadas consistentes, chame o método ValidationProblem em vez de BadRequest. ValidationProblem retorna um objeto ValidationProblemDetails, bem como a resposta automática.

Registrar respostas de 400 automática

Para registrar 400 respostas automáticas, defina a propriedade delegada InvalidModelStateResponseFactory para executar o processamento personalizado no Startup.ConfigureServices. Por padrão, InvalidModelStateResponseFactory usa ProblemDetailsFactory para criar uma instância de ValidationProblemDetails.

O exemplo a seguir mostra como recuperar uma instância de ILogger<TCategoryName> para registrar informações sobre uma resposta 400 automática:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Startup>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails response.
            // To produce a custom response, return a different implementation of IActionResult instead.
            return builtInFactory(context);
        };
    });

Desabilitar resposta de 400 automática

Para desabilitar o comportamento 400 automático, defina a propriedade SuppressModelStateInvalidFilter como true. Adicione o código realçado a seguir a Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Inferência de parâmetro de origem da associação

Um atributo de origem de associação define o local no qual o valor do parâmetro de uma ação é encontrado. Os seguintes atributos da origem da associação existem:

Atributo Origem de associação
[FromBody] Corpo da solicitação
[FromForm] Dados do formulário no corpo da solicitação
[FromHeader] Cabeçalho da solicitação
[FromQuery] Parâmetro de cadeia de caracteres de consulta de solicitação
[FromRoute] Dados de rota da solicitação atual
[FromServices] O serviço de solicitação inserido como um parâmetro de ação

Aviso

Não use [FromRoute] quando os valores puderem conter %2f (ou seja, /). %2f não ficará sem escape para /. Use [FromQuery], se o valor puder conter %2f.

Sem o atributo [ApiController] ou outros atributos de origem da associação, como [FromQuery], o runtime do ASP.NET Core tenta usar o associador de modelos de objeto complexo. O associador de modelos de objeto complexo extrai os dados dos provedores de valor em uma ordem definida.

No exemplo a seguir, o atributo [FromQuery] indica que o valor do parâmetro discontinuedOnly é fornecido na cadeia de caracteres de consulta da URL de solicitação:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

O atributo [ApiController] aplica regras de inferência para as fontes de dados padrão dos parâmetros de ação. Essas regras poupam você da necessidade de identificar as origens de associação manualmente aplicando atributos aos parâmetros de ação. As regras de inferência da origem de associação se comportam da seguinte maneira:

  • [FromBody] é inferido para parâmetros de tipo complexo. Uma exceção à regra de inferência [FromBody] é qualquer tipo interno complexo com um significado especial, como IFormCollection e CancellationToken. O código de inferência da origem da associação ignora esses tipos especiais.
  • [FromForm] é inferido para parâmetros de ação do tipo IFormFile e IFormFileCollection. Ele não é inferido para qualquer tipo simples ou definido pelo usuário.
  • [FromRoute] é inferido para qualquer nome de parâmetro de ação correspondente a um parâmetro no modelo de rota. Quando mais de uma rota correspondem a um parâmetro de ação, qualquer valor de rota é considerado [FromRoute].
  • [FromQuery] é inferido para todos os outros parâmetros de ação.

Notas de inferência FromBody

[FromBody] não é inferido para tipos simples, como string ou int. Portanto, o atributo [FromBody] deve ser usado para tipos simples quando essa funcionalidade for necessária.

Quando uma ação tiver mais de um parâmetro associado ao corpo da solicitação, uma exceção será lançada. Por exemplo, todas as assinaturas de método de ação a seguir causam uma exceção:

  • [FromBody] inferido em ambos, pois são tipos complexos.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • O atributo [FromBody] em um, inferido no outro, porque é um tipo complexo.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Atributo [FromBody] em ambos.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Desabilitar regras de inferência

Para desabilitar a inferência da origem da associação, defina SuppressInferBindingSourcesForParameters como true. Adicione o seguinte código em Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Inferência de solicitação de várias partes/dados de formulário

O atributo [ApiController] aplica uma regra de inferência para parâmetros de ação do tipo IFormFile e IFormFileCollection. O tipo de conteúdo da solicitação multipart/form-data é inferido para esses tipos.

Para desabilitar o comportamento padrão, defina a propriedade SuppressConsumesConstraintForFormFileParameters como true no Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Detalhes do problema dos códigos de status de erro

O MVC transforma um resultado de erro (um resultado com o código de status 400 ou superior) em um resultado com ProblemDetails. O tipo ProblemDetails tem base na especificação RFC 7807 para fornecer detalhes de erro legíveis por computador em uma resposta HTTP.

Considere o seguinte código em uma ação do controlador:

if (pet == null)
{
    return NotFound();
}

O método NotFound produz um código de status HTTP 404 com um corpo ProblemDetails. Por exemplo:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Desabilitar a resposta de ProblemDetails

A criação automática de um ProblemDetails para códigos de status de erro fica desabilitada quando a propriedade SuppressMapClientErrors é definida como true. Adicione o seguinte código em Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Definir os tipos de conteúdo de solicitação com suporte com o atributo [Consome]

Por padrão, uma ação dá suporte a todos os tipos de conteúdo de solicitação disponíveis. Por exemplo, se um aplicativo estiver configurado para dar suporte aos formatadores de entrada de JSON e XML, uma ação dará suporte a vários tipos de conteúdo, incluindo application/json e application/xml.

O atributo [Consome] permite que uma ação limite os tipos de conteúdo de solicitação com suporte. Aplique o atributo [Consumes] a uma ação ou controlador, especificando um ou mais tipos de conteúdo:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

No código anterior, a ação CreateProduct especifica o tipo de conteúdo application/xml. As solicitações roteadas para essa ação devem especificar um cabeçalho Content-Type de application/xml. Solicitações que não especificam um cabeçalho Content-Type de application/xml resultam em uma resposta Tipo de Mídia Sem Suporte 415.

O atributo [Consumes] também permite que uma ação influencie sua seleção com base no tipo de conteúdo de uma solicitação de entrada, aplicando uma restrição de tipo. Considere o seguinte exemplo:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

No código anterior, ConsumesController está configurado para lidar com solicitações enviadas para a URL https://localhost:5001/api/Consumes. Ambas as ações do controlador, PostJson e PostForm, tratam de solicitações POST com a mesma URL. Sem o atributo [Consumes] que aplica uma restrição de tipo, uma exceção de correspondência ambígua é lançada.

O atributo [Consumes] é aplicado a ambas as ações. A ação PostJson trata as solicitações enviadas com um cabeçalho Content-Type de application/json. A ação PostForm trata as solicitações enviadas com um cabeçalho Content-Type de application/x-www-form-urlencoded.

Recursos adicionais

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Não crie um controlador de API Web derivando da classe base Controller. Controller é derivado de ControllerBase e agrega suporte para exibições; portanto, serve para manipulação de páginas da Web, não para solicitações de API Web. Há uma exceção a essa regra: se você planeja usar o mesmo controlador para visualizações e APIs Web, deduza-o de Controller. A classe ControllerBase fornece muitas propriedades e métodos úteis para lidar com solicitações HTTP. Por exemplo, ControllerBase.CreatedAtAction retorna um código de status 201:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Confira mais alguns exemplos de métodos fornecidos por ControllerBase:

Método Observações
BadRequest Retorna o código de status 400.
NotFound Retorna o código de status 404.
PhysicalFile Retorna um arquivo.
TryUpdateModelAsync Invoca model binding.
TryValidateModel Invoca validação de modelo.

Confira uma lista com todos os métodos e propriedades disponíveis em ControllerBase.

Atributos

O namespace Microsoft.AspNetCore.Mvc fornece atributos que podem ser usados para configurar o comportamento de controladores de API Web e dos métodos de ação. O exemplo a seguir usa atributos para especificar o verbo de ação HTTP compatível e quaisquer códigos de status HTTP conhecidos que possam ser retornados:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Aqui estão mais alguns exemplos de atributos que estão disponíveis:

Atributo Observações
[Route] Especifica o padrão de URL para um controlador ou ação.
[Bind] Especifica o prefixo e as propriedades que serão incluídos no model binding.
[HttpGet] Identifica uma ação que dá suporte ao verbo de ação HTTP GET.
[Consumes] Especifica os tipos de dados aceitos por uma ação.
[Produces] Especifica os tipos de dados retornados por uma ação.

Veja uma lista que inclui os atributos disponíveis no namespace Microsoft.AspNetCore.Mvc.

Atributo ApiController

O atributo [ApiController] pode ser aplicado a uma classe de controlador para permitir os seguintes comportamentos opinativos específicos da API:

Atributo em controladores específicos

O atributo [ApiController] pode ser aplicado a controladores específicos, como no exemplo a seguir do modelo de projeto:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Atributo em vários controladores

Uma abordagem ao uso do atributo em mais de um controlador é a criação de uma classe de controlador base personalizada anotada com o atributo [ApiController]. O exemplo a seguir mostra uma classe base personalizada e um controlador derivado dela:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("api/[controller]")]
public class PetsController : MyControllerBase

Atributo em um assembly

Se versão de compatibilidade estiver definida como 2.2 ou posterior, o atributo [ApiController] poderá ser aplicado a um assembly. A anotação dessa maneira aplica o comportamento da API Web para todos os controladores no assembly. Não é possível recusar controladores individuais. Aplique o atributo de nível de assembly à declaração de namespace em torno da classe Startup:

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

Requisito de roteamento de atributo

O atributo [ApiController] transforma em requisito o roteamento de atributo. Por exemplo:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

As ações são inacessíveis por meio de rotas convencionais definidas por UseMvc ou UseMvcWithDefaultRoute em Startup.Configure.

Respostas HTTP 400 automáticas

O atributo [ApiController] faz com que os erros de validação do modelo disparem automaticamente uma resposta HTTP 400. Consequentemente, o código a seguir se torna desnecessário em um método de ação:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

O MVC do ASP.NET Core usa o filtro de ação ModelStateInvalidFilter para fazer a verificação anterior.

Resposta BadRequest padrão

Com uma versão de compatibilidade de 2.1, o tipo de resposta padrão para uma resposta HTTP 400 é SerializableError. O corpo da solicitação a seguir é um exemplo do tipo serializado:

{
  "": [
    "A non-empty request body is required."
  ]
}

Com uma versão de compatibilidade de 2.2 ou posterior, o tipo de resposta padrão para uma resposta HTTP 400 é ValidationProblemDetails. O corpo da solicitação a seguir é um exemplo do tipo serializado:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

O tipo ValidationProblemDetails:

  • Fornece um formato legível por computador para especificar erros nas respostas da API Web.
  • Em conformidade com a especificação RFC 7807.

Para tornar as respostas automáticas e personalizadas consistentes, chame o método ValidationProblem em vez de BadRequest. ValidationProblem retorna um objeto ValidationProblemDetails, bem como a resposta automática.

Registrar respostas de 400 automática

Confira Como registrar respostas de 400 automática sobre erros de validação de modelo (dotnet/AspNetCore.Docs #12157).

Desabilitar resposta de 400 automática

Para desabilitar o comportamento 400 automático, defina a propriedade SuppressModelStateInvalidFilter como true. Adicione o código realçado a seguir a Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Inferência de parâmetro de origem da associação

Um atributo de origem de associação define o local no qual o valor do parâmetro de uma ação é encontrado. Os seguintes atributos da origem da associação existem:

Atributo Origem de associação
[FromBody] Corpo da solicitação
[FromForm] Dados do formulário no corpo da solicitação
[FromHeader] Cabeçalho da solicitação
[FromQuery] Parâmetro de cadeia de caracteres de consulta de solicitação
[FromRoute] Dados de rota da solicitação atual
[FromServices] O serviço de solicitação inserido como um parâmetro de ação

Aviso

Não use [FromRoute] quando os valores puderem conter %2f (ou seja, /). %2f não ficará sem escape para /. Use [FromQuery], se o valor puder conter %2f. Sem o atributo [ApiController] ou outros atributos de origem da associação, como [FromQuery], o runtime do ASP.NET Core tenta usar o associador de modelos de objeto complexo. O associador de modelos de objeto complexo extrai os dados dos provedores de valor em uma ordem definida.

No exemplo a seguir, o atributo [FromQuery] indica que o valor do parâmetro discontinuedOnly é fornecido na cadeia de caracteres de consulta da URL de solicitação:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

O atributo [ApiController] aplica regras de inferência para as fontes de dados padrão dos parâmetros de ação. Essas regras poupam você da necessidade de identificar as origens de associação manualmente aplicando atributos aos parâmetros de ação. As regras de inferência da origem de associação se comportam da seguinte maneira:

  • [FromBody] é inferido para parâmetros de tipo complexo. Uma exceção à regra de inferência [FromBody] é qualquer tipo interno complexo com um significado especial, como IFormCollection e CancellationToken. O código de inferência da origem da associação ignora esses tipos especiais.
  • [FromForm] é inferido para parâmetros de ação do tipo IFormFile e IFormFileCollection. Ele não é inferido para qualquer tipo simples ou definido pelo usuário.
  • [FromRoute] é inferido para qualquer nome de parâmetro de ação correspondente a um parâmetro no modelo de rota. Quando mais de uma rota correspondem a um parâmetro de ação, qualquer valor de rota é considerado [FromRoute].
  • [FromQuery] é inferido para todos os outros parâmetros de ação.

Notas de inferência FromBody

[FromBody] não é inferido para tipos simples, como string ou int. Portanto, o atributo [FromBody] deve ser usado para tipos simples quando essa funcionalidade for necessária.

Quando uma ação tiver mais de um parâmetro associado ao corpo da solicitação, uma exceção será lançada. Por exemplo, todas as assinaturas de método de ação a seguir causam uma exceção:

  • [FromBody] inferido em ambos, pois são tipos complexos.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • O atributo [FromBody] em um, inferido no outro, porque é um tipo complexo.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Atributo [FromBody] em ambos.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Observação

No ASP.NET Core 2.1, os parâmetros de tipo de coleção, como listas e matrizes, são inferidos incorretamente como [FromQuery]. O atributo [FromBody] deve ser usado para esses parâmetros se eles forem vinculados ao corpo da solicitação. Esse comportamento é corrigido no ASP.NET Core 2.2 ou posterior, onde os parâmetros do tipo de coleção são inferidos para serem associados ao corpo por padrão.

Desabilitar regras de inferência

Para desabilitar a inferência da origem da associação, defina SuppressInferBindingSourcesForParameters como true. Adicione o seguinte código em Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Inferência de solicitação de várias partes/dados de formulário

O atributo [ApiController] aplica uma regra de inferência para parâmetros de ação do tipo IFormFile e IFormFileCollection. O tipo de conteúdo da solicitação multipart/form-data é inferido para esses tipos. Para desabilitar o comportamento padrão, defina a propriedade SuppressConsumesConstraintForFormFileParameters como true no Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Detalhes do problema dos códigos de status de erro

Quando a versão de compatibilidade for 2.2 ou posterior, o MVC transformará um resultado de erro (um resultado com o código de status 400 ou superior) em um resultado com ProblemDetails. O tipo ProblemDetails tem base na especificação RFC 7807 para fornecer detalhes de erro legíveis por computador em uma resposta HTTP. Considere o seguinte código em uma ação do controlador:

if (pet == null)
{
    return NotFound();
}

O método NotFound produz um código de status HTTP 404 com um corpo ProblemDetails. Por exemplo:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Desabilitar a resposta de ProblemDetails

A criação automática de um ProblemDetails para códigos de status de erro fica desabilitada quando a propriedade SuppressMapClientErrors é definida como true. Adicione o seguinte código em Startup.ConfigureServices:

Definir os tipos de conteúdo de solicitação com suporte com o atributo [Consome]

Por padrão, uma ação dá suporte a todos os tipos de conteúdo de solicitação disponíveis. Por exemplo, se um aplicativo estiver configurado para dar suporte aos formatadores de entrada de JSON e XML, uma ação dará suporte a vários tipos de conteúdo, incluindo application/json e application/xml.

O atributo [Consome] permite que uma ação limite os tipos de conteúdo de solicitação com suporte. Aplique o atributo [Consumes] a uma ação ou controlador, especificando um ou mais tipos de conteúdo:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

No código anterior, a ação CreateProduct especifica o tipo de conteúdo application/xml. As solicitações roteadas para essa ação devem especificar um cabeçalho Content-Type de application/xml. Solicitações que não especificam um cabeçalho Content-Type de application/xml resultam em uma resposta Tipo de Mídia Sem Suporte 415. O atributo [Consumes] também permite que uma ação influencie sua seleção com base no tipo de conteúdo de uma solicitação de entrada, aplicando uma restrição de tipo. Considere o seguinte exemplo:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

No código anterior, ConsumesController está configurado para lidar com solicitações enviadas para a URL https://localhost:5001/api/Consumes. Ambas as ações do controlador, PostJson e PostForm, tratam de solicitações POST com a mesma URL. Sem o atributo [Consumes] que aplica uma restrição de tipo, uma exceção de correspondência ambígua é lançada. O atributo [Consumes] é aplicado a ambas as ações. A ação PostJson trata as solicitações enviadas com um cabeçalho Content-Type de application/json. A ação PostForm trata as solicitações enviadas com um cabeçalho Content-Type de application/x-www-form-urlencoded.

Recursos adicionais