Creación de API web con ASP.NET Core

ASP.NET Core admite la creación de API web mediante controladores o mediante API mínimas. Los controladores de una API web son clases que se derivan de ControllerBase. Los controladores se activan y eliminan por solicitud.

En este artículo se muestra cómo usar controladores para gestionar las solicitudes de API web. Para obtener información sobre cómo crear API web sin controladores, consulte Tutorial: Creación de una API mínima con ASP.NET Core.

Clase ControllerBase

Una API web basada en controlador consta de una o varias clases de controlador que se derivan de ControllerBase. La plantilla de proyecto de API web proporciona un controlador de inicio:

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

Normalmente, los controladores de API web deben derivarse de ControllerBase, en lugar de Controller. Controller se deriva de ControllerBase y agrega compatibilidad con vistas, por lo que sirve para gestionar páginas web, no solicitudes de API web. Si el mismo controlador debe admitir vistas y API web, debe derivarse de Controller.

La clase ControllerBase ofrece muchas propiedades y métodos que son útiles para gestionar solicitudes HTTP. Por ejemplo, CreatedAtAction devuelve un código de estado 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);
}

En la tabla siguiente se incluyen ejemplos de métodos en ControllerBase.

Método Notas
BadRequest Devuelve el código de estado 400.
NotFound Devuelve el código de estado 404.
PhysicalFile Devuelve un archivo.
TryUpdateModelAsync Invoca el enlace de modelo.
TryValidateModel Invoca la validación de modelos.

Para ver una lista de todos los métodos y propiedades disponibles, consulte ControllerBase.

Atributos

El espacio de nombres Microsoft.AspNetCore.Mvc proporciona atributos que se pueden usar para configurar el comportamiento de los controladores API web y los métodos de acción. En el ejemplo siguiente se usan atributos para especificar el verbo de acción HTTP admitido y cualquier código de estado HTTP conocido que se pueda devolver:

[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);
}

Estos son algunos ejemplos más de atributos que están disponibles.

Atributo Notas
[Route] Especifica el patrón de dirección URL de un controlador o una acción.
[Bind] Especifica el prefijo y las propiedades que se incluirán en el enlace de modelo.
[HttpGet] Identifica una acción que admite el verbo de acción GET HTTP.
[Consumes] Especifica los tipos de datos que acepta una acción.
[Produces] Especifica los tipos de datos que devuelve una acción.

Para ver una lista que incluye los atributos disponibles, consulte el espacio de nombres Microsoft.AspNetCore.Mvc.

Atributo ApiController

El atributo [ApiController] se puede aplicar a una clase de controlador para permitir los siguientes comportamientos específicos de la API:

Atributo en controladores específicos

El atributo [ApiController] puede aplicarse a controladores específicos, como se muestra en el siguiente ejemplo de la plantilla de proyecto:

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

Atributo en varios controladores

Una estrategia para el uso del atributo en más de un controlador consiste en crear una clase personalizada de controlador base anotada con el atributo [ApiController]. En el siguiente ejemplo se muestra una clase base personalizada y un controlador que se deriva de ella:

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

Atributo en un ensamblado

El atributo [ApiController] puede aplicarse a un ensamblado. Cuando el atributo [ApiController] se aplica a un ensamblado, todos los controladores del ensamblado tienen aplicado el atributo [ApiController]. No hay ninguna manera de excluir controladores específicos. Aplique el atributo de nivel de ensamblado al archivo 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 enrutamiento mediante atributos

El atributo [ApiController] convierte el enrutamiento de atributos en un requisito. Por ejemplo:

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

No es posible acceder a las acciones mediante rutas convencionales que hayan definido UseEndpoints, UseMvc o UseMvcWithDefaultRoute.

Respuestas HTTP 400 automáticas

El atributo [ApiController] hace que los errores de validación de un modelo desencadenen automáticamente una respuesta HTTP 400. Por lo tanto, el siguiente código no es necesario en un método de acción:

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

ASP.NET Core MVC usa el filtro de acciones ModelStateInvalidFilter para realizar la comprobación anterior.

Respuesta BadRequest predeterminada

El tipo de respuesta predeterminado para una respuesta HTTP 400 es ValidationProblemDetails. El siguiente cuerpo de respuesta es un ejemplo del 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."
    ]
  }
}

Tipo ValidationProblemDetails:

  • Proporciona un formato de lectura mecánica para especificar errores en las respuestas de la API web.
  • Cumple los requisitos de la especificación RFC 7807.

Para que las respuestas automáticas y personalizadas sean coherentes, llame al método ValidationProblem en lugar de a BadRequest. ValidationProblem devuelve un objeto ValidationProblemDetails y la respuesta automática.

Registro de respuestas 400 automáticas

Para registrar respuestas 400 automáticas, establezca la propiedad delegada InvalidModelStateResponseFactory para realizar el procesamiento personalizado. De forma predeterminada, InvalidModelStateResponseFactory utiliza ProblemDetailsFactory para crear una instancia de ValidationProblemDetails.

En el ejemplo siguiente se muestra cómo recuperar una instancia de ILogger<TCategoryName> para registrar información sobre una respuesta 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();

Deshabilitación de las respuestas 400 automáticas

Para deshabilitar el comportamiento 400 automático, establezca la propiedad SuppressModelStateInvalidFilter en true. Agregue el código resaltado siguiente:

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

Inferencia de parámetro de origen de enlace

Un atributo de origen de enlace define la ubicación del valor del parámetro de una acción. Existen los atributos de origen de enlace siguientes:

Atributo Origen de enlace
[FromBody] Cuerpo de la solicitud
[FromForm] Datos del formulario en el cuerpo de la solicitud
[FromHeader] Encabezado de la solicitud
[FromQuery] Parámetro de la cadena de consulta de la solicitud
[FromRoute] Datos de ruta de la solicitud actual
[FromServices] Servicio de solicitud insertado como parámetro de acción
[AsParameters] Parámetros de métodos

Advertencia

No use [FromRoute] si los valores pueden contener %2f (es decir, /). %2f no incluirá el carácter sin escape /. Use [FromQuery] si el valor puede contener %2f.

Sin el atributo [ApiController] o los atributos de origen de enlace, como [FromQuery], el entorno de tiempo de ejecución de ASP.NET Core intenta usar el enlazador de modelos de objetos complejos. El enlazador de modelos de objetos complejos extrae los datos de los proveedores de valor en un orden definido.

En el ejemplo siguiente, el atributo [FromQuery] indica que el valor del parámetro discontinuedOnly se proporciona en la cadena de consulta de la dirección URL de la solicitud:

[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;
}

El atributo [ApiController] aplica reglas de inferencia a los orígenes de datos predeterminados de los parámetros de acción. Estas reglas aplican atributos a los parámetros de acción, lo que ahorra la necesidad de identificar los orígenes de enlace manualmente. Las reglas de inferencia de orígenes de enlace se comportan de la manera siguiente:

  • [FromServices] se infiere para parámetros de tipo complejos registrados en el contenedor de inserción de dependencias.
  • [FromBody] se infiere para parámetros de tipo complejos no registrados en el contenedor de inserción de dependencias. La excepción a [FromBody] esta regla es cualquier tipo integrado complejo que tenga un significado especial, como IFormCollection y CancellationToken. El código de inferencia del origen de enlace omite esos tipos especiales.
  • [FromForm] se infiere para los parámetros de acción de tipo IFormFile y IFormFileCollection. No se infiere para los tipos simples o definidos por el usuario.
  • [FromRoute] se infiere para cualquier nombre de parámetro de acción que coincida con un parámetro de la plantilla de ruta. Si varias rutas coinciden con un parámetro de acción, cualquier valor de ruta se considera [FromRoute].
  • [FromQuery] se infiere para cualquier otro parámetro de acción.

Notas de la inferencia de FromBody

En el caso de los tipos simples, como string y int, [FromBody] no se infiere. Así pues, para los tipos simples, en los casos en los que quiera utilizar dicha funcionalidad, conviene usar el atributo [FromBody].

Cuando una acción tiene más de un parámetro enlazado desde el cuerpo de solicitud, se produce una excepción. Por ejemplo, todas las firmas de acción siguientes provocan una excepción:

  • [FromBody] se infiere en ambos porque son tipos complejos.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • El atributo [FromBody] en uno se infiere en el otro porque es un tipo complejo.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • El atributo [FromBody] se infiere en ambos.

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

Notas de inferencia de FromServices

El enlace de parámetros enlaza parámetros mediante la inserción de dependencias cuando el tipo está configurado como servicio. Esto significa que no es necesario aplicar explícitamente el atributo [FromServices] a un parámetro. En el código siguiente, ambas acciones devuelven la 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);
}

En casos aislados, la inserción automática de dependencias puede interrumpir las aplicaciones que tienen un tipo en la DI que también se acepta en los métodos de las acciones del controlador de API. No es habitual tener un tipo en la DI y como argumento en una acción del controlador de API.

Para deshabilitar la inferencia de [FromServices] para un único parámetro de acción, aplique el atributo de origen de enlace deseado al parámetro. Por ejemplo, aplique el atributo [FromBody] a un parámetro de acción que se debe enlazar desde el cuerpo de la solicitud.

Para deshabilitar la inferencia [FromServices] globalmente, establezca DisableImplicitFromServicesParameters en 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();

Los tipos se comprueban al iniciar la aplicación con IServiceProviderIsService a fin de determinar si un argumento de una acción del controlador de API procede de la inserción de dependencias o de otros orígenes.

El mecanismo para deducir el origen del enlace de los parámetros de la acción del controlador de API utiliza estas reglas:

Deshabilitación de las reglas de inferencia

Para deshabilitar la inferencia del origen de enlace, establezca SuppressInferBindingSourcesForParameters en 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();

Inferencia de solicitud de varios elementos o datos de formulario

El atributo [ApiController] aplica una regla de inferencia para los parámetros de acción de tipo IFormFile y IFormFileCollection. El tipo de contenido de la solicitud multipart/form-data se infiere para estos tipos.

Para deshabilitar el comportamiento predeterminado, establezca la propiedad SuppressConsumesConstraintForFormFileParameters en 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();

Detalles de problemas de los códigos de estado de error

MVC transforma un resultado de error (un resultado con un código de estado 400 o superior) en un resultado con ProblemDetails. El tipo ProblemDetails se basa en la especificación RFC 7807 para proporcionar detalles de error de lectura mecánica en una respuesta HTTP.

Observe el código siguiente en una acción de controlador:

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

El método NotFound genera un código de estado HTTP 404 con un cuerpo ProblemDetails. Por ejemplo:

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

Deshabilitación de la respuesta ProblemDetails

La creación automática de ProblemDetails para los códigos de estado de error está deshabilitada cuando la propiedad SuppressMapClientErrors está establecida en true. Agregue el siguiente 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();

Definición de tipos de contenido de la solicitud compatibles con el atributo [Consumes]

De forma predeterminada, una acción admite todos los tipos de contenido de la solicitud disponibles. Por ejemplo, si una aplicación está configurada para admitir formateadores de entradaJSON y XML, una acción admite varios tipos de contenido, incluidos application/json y application/xml.

El atributo [Consumes] permite que una acción limite los tipos de contenido de la solicitud compatibles. Aplique el atributo [Consumes] a una acción o controlador, especificando uno o varios tipos de contenido:

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

En el código anterior, la acción CreateProduct especifica el tipo de contenido application/xml. Las solicitudes enrutadas a esta acción deben especificar un encabezado Content-Type de application/xml. Las solicitudes que no especifican un encabezado Content-Type de application/xml generan una respuesta 415 Tipo de medio no compatible.

El atributo [Consumes] también permite que una acción influya en su selección en función del tipo de contenido de una solicitud entrante aplicando una restricción de tipo. Considere el ejemplo siguiente:

[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 });
}

En el código anterior, ConsumesController se configura para controlar las solicitudes enviadas a la dirección URL https://localhost:5001/api/Consumes. Las dos acciones del controlador, PostJson y PostForm, controlan las solicitudes POST con la misma dirección URL. Sin el atributo [Consumes] que aplica una restricción de tipo, se produce una excepción de coincidencia ambigua.

El atributo [Consumes] se aplica a ambas acciones. La acción PostJson controla las solicitudes enviadas con un encabezado Content-Type de application/json. La acción PostForm controla las solicitudes enviadas con un encabezado Content-Type de application/x-www-form-urlencoded.

Recursos adicionales

ASP.NET Core admite la creación de API web mediante controladores o mediante API mínimas. Los controladores de una API web son clases que se derivan de ControllerBase. En este artículo se muestra cómo usar controladores para gestionar las solicitudes de API web. Para obtener información sobre cómo crear API web sin controladores, consulte Tutorial: Creación de una API mínima con ASP.NET Core.

Clase ControllerBase

Una API web basada en controlador consta de una o varias clases de controlador que se derivan de ControllerBase. La plantilla de proyecto de API web proporciona un controlador de inicio:

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

Normalmente, los controladores de API web deben derivarse de ControllerBase, en lugar de Controller. Controller se deriva de ControllerBase y agrega compatibilidad con vistas, por lo que sirve para gestionar páginas web, no solicitudes de API web. Si el mismo controlador debe admitir vistas y API web, debe derivarse de Controller.

La clase ControllerBase ofrece muchas propiedades y métodos que son útiles para gestionar solicitudes HTTP. Por ejemplo, CreatedAtAction devuelve un código de estado 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);
}

En la tabla siguiente se incluyen ejemplos de métodos en ControllerBase.

Método Notas
BadRequest Devuelve el código de estado 400.
NotFound Devuelve el código de estado 404.
PhysicalFile Devuelve un archivo.
TryUpdateModelAsync Invoca el enlace de modelo.
TryValidateModel Invoca la validación de modelos.

Para ver una lista de todos los métodos y propiedades disponibles, consulte ControllerBase.

Atributos

El espacio de nombres Microsoft.AspNetCore.Mvc proporciona atributos que se pueden usar para configurar el comportamiento de los controladores API web y los métodos de acción. En el ejemplo siguiente se usan atributos para especificar el verbo de acción HTTP admitido y cualquier código de estado HTTP conocido que se pueda devolver:

[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);
}

Estos son algunos ejemplos más de atributos que están disponibles.

Atributo Notas
[Route] Especifica el patrón de dirección URL de un controlador o una acción.
[Bind] Especifica el prefijo y las propiedades que se incluirán en el enlace de modelo.
[HttpGet] Identifica una acción que admite el verbo de acción GET HTTP.
[Consumes] Especifica los tipos de datos que acepta una acción.
[Produces] Especifica los tipos de datos que devuelve una acción.

Para ver una lista que incluye los atributos disponibles, consulte el espacio de nombres Microsoft.AspNetCore.Mvc.

Atributo ApiController

El atributo [ApiController] se puede aplicar a una clase de controlador para permitir los siguientes comportamientos específicos de la API:

Atributo en controladores específicos

El atributo [ApiController] puede aplicarse a controladores específicos, como se muestra en el siguiente ejemplo de la plantilla de proyecto:

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

Atributo en varios controladores

Una estrategia para el uso del atributo en más de un controlador consiste en crear una clase personalizada de controlador base anotada con el atributo [ApiController]. En el siguiente ejemplo se muestra una clase base personalizada y un controlador que se deriva de ella:

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

Atributo en un ensamblado

El atributo [ApiController] puede aplicarse a un ensamblado. Cuando el atributo [ApiController] se aplica a un ensamblado, todos los controladores del ensamblado tienen aplicado el atributo [ApiController]. No hay ninguna manera de excluir controladores específicos. Aplique el atributo de nivel de ensamblado al archivo 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 enrutamiento mediante atributos

El atributo [ApiController] convierte el enrutamiento de atributos en un requisito. Por ejemplo:

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

No es posible acceder a las acciones mediante rutas convencionales que hayan definido UseEndpoints, UseMvc o UseMvcWithDefaultRoute.

Respuestas HTTP 400 automáticas

El atributo [ApiController] hace que los errores de validación de un modelo desencadenen automáticamente una respuesta HTTP 400. Por lo tanto, el siguiente código no es necesario en un método de acción:

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

ASP.NET Core MVC usa el filtro de acciones ModelStateInvalidFilter para realizar la comprobación anterior.

Respuesta BadRequest predeterminada

El siguiente cuerpo de respuesta es un ejemplo del tipo serializado:

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

El tipo de respuesta predeterminado para una respuesta HTTP 400 es ValidationProblemDetails. El siguiente cuerpo de respuesta es un ejemplo del 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."
    ]
  }
}

Tipo ValidationProblemDetails:

  • Proporciona un formato de lectura mecánica para especificar errores en las respuestas de la API web.
  • Cumple los requisitos de la especificación RFC 7807.

Para que las respuestas automáticas y personalizadas sean coherentes, llame al método ValidationProblem en lugar de a BadRequest. ValidationProblem devuelve un objeto ValidationProblemDetails y la respuesta automática.

Registro de respuestas 400 automáticas

Para registrar respuestas 400 automáticas, establezca la propiedad delegada InvalidModelStateResponseFactory para realizar el procesamiento personalizado. De forma predeterminada, InvalidModelStateResponseFactory utiliza ProblemDetailsFactory para crear una instancia de ValidationProblemDetails.

En el ejemplo siguiente se muestra cómo recuperar una instancia de ILogger<TCategoryName> para registrar información sobre una respuesta 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();

Deshabilitación de las respuestas 400 automáticas

Para deshabilitar el comportamiento 400 automático, establezca la propiedad SuppressModelStateInvalidFilter en true. Agregue el código resaltado siguiente:

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

Inferencia de parámetro de origen de enlace

Un atributo de origen de enlace define la ubicación del valor del parámetro de una acción. Existen los atributos de origen de enlace siguientes:

Atributo Origen de enlace
[FromBody] Cuerpo de la solicitud
[FromForm] Datos del formulario en el cuerpo de la solicitud
[FromHeader] Encabezado de la solicitud
[FromQuery] Parámetro de la cadena de consulta de la solicitud
[FromRoute] Datos de ruta de la solicitud actual
[FromServices] Servicio de solicitud insertado como parámetro de acción

Advertencia

No use [FromRoute] si los valores pueden contener %2f (es decir, /). %2f no incluirá el carácter sin escape /. Use [FromQuery] si el valor puede contener %2f.

Sin el atributo [ApiController] o los atributos de origen de enlace, como [FromQuery], el entorno de tiempo de ejecución de ASP.NET Core intenta usar el enlazador de modelos de objetos complejos. El enlazador de modelos de objetos complejos extrae los datos de los proveedores de valor en un orden definido.

En el ejemplo siguiente, el atributo [FromQuery] indica que el valor del parámetro discontinuedOnly se proporciona en la cadena de consulta de la dirección URL de la solicitud:

[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;
}

El atributo [ApiController] aplica reglas de inferencia a los orígenes de datos predeterminados de los parámetros de acción. Estas reglas aplican atributos a los parámetros de acción, lo que ahorra la necesidad de identificar los orígenes de enlace manualmente. Las reglas de inferencia de orígenes de enlace se comportan de la manera siguiente:

  • [FromBody] se infiere para parámetros de tipo complejos no registrados en el contenedor de inserción de dependencias. La excepción a [FromBody] esta regla es cualquier tipo integrado complejo que tenga un significado especial, como IFormCollection y CancellationToken. El código de inferencia del origen de enlace omite esos tipos especiales.
  • [FromForm] se infiere para los parámetros de acción de tipo IFormFile y IFormFileCollection. No se infiere para los tipos simples o definidos por el usuario.
  • [FromRoute] se infiere para cualquier nombre de parámetro de acción que coincida con un parámetro de la plantilla de ruta. Si varias rutas coinciden con un parámetro de acción, cualquier valor de ruta se considera [FromRoute].
  • [FromQuery] se infiere para cualquier otro parámetro de acción.

Notas de la inferencia de FromBody

En el caso de los tipos simples, como string y int, [FromBody] no se infiere. Así pues, para los tipos simples, en los casos en los que quiera utilizar dicha funcionalidad, conviene usar el atributo [FromBody].

Cuando una acción tiene más de un parámetro enlazado desde el cuerpo de solicitud, se produce una excepción. Por ejemplo, todas las firmas de acción siguientes provocan una excepción:

  • [FromBody] se infiere en ambos porque son tipos complejos.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • El atributo [FromBody] en uno se infiere en el otro porque es un tipo complejo.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • El atributo [FromBody] se infiere en ambos.

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

Deshabilitación de las reglas de inferencia

Para deshabilitar la inferencia del origen de enlace, establezca SuppressInferBindingSourcesForParameters en 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();

Inferencia de solicitud de varios elementos o datos de formulario

El atributo [ApiController] aplica una regla de inferencia para los parámetros de acción de tipo IFormFile y IFormFileCollection. El tipo de contenido de la solicitud multipart/form-data se infiere para estos tipos.

Para deshabilitar el comportamiento predeterminado, establezca la propiedad SuppressConsumesConstraintForFormFileParameters en 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();

Detalles de problemas de los códigos de estado de error

MVC transforma un resultado de error (un resultado con un código de estado 400 o superior) en un resultado con ProblemDetails. El tipo ProblemDetails se basa en la especificación RFC 7807 para proporcionar detalles de error de lectura mecánica en una respuesta HTTP.

Observe el código siguiente en una acción de controlador:

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

El método NotFound genera un código de estado HTTP 404 con un cuerpo ProblemDetails. Por ejemplo:

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

Deshabilitación de la respuesta ProblemDetails

La creación automática de ProblemDetails para los códigos de estado de error está deshabilitada cuando la propiedad SuppressMapClientErrors está establecida en 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();

Definición de tipos de contenido de la solicitud compatibles con el atributo [Consumes]

De forma predeterminada, una acción admite todos los tipos de contenido de la solicitud disponibles. Por ejemplo, si una aplicación está configurada para admitir formateadores de entradaJSON y XML, una acción admite varios tipos de contenido, incluidos application/json y application/xml.

El atributo [Consumes] permite que una acción limite los tipos de contenido de la solicitud compatibles. Aplique el atributo [Consumes] a una acción o controlador, especificando uno o varios tipos de contenido:

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

En el código anterior, la acción CreateProduct especifica el tipo de contenido application/xml. Las solicitudes enrutadas a esta acción deben especificar un encabezado Content-Type de application/xml. Las solicitudes que no especifican un encabezado Content-Type de application/xml generan una respuesta 415 Tipo de medio no compatible.

El atributo [Consumes] también permite que una acción influya en su selección en función del tipo de contenido de una solicitud entrante aplicando una restricción de tipo. Considere el ejemplo siguiente:

[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 });
}

En el código anterior, ConsumesController se configura para controlar las solicitudes enviadas a la dirección URL https://localhost:5001/api/Consumes. Las dos acciones del controlador, PostJson y PostForm, controlan las solicitudes POST con la misma dirección URL. Sin el atributo [Consumes] que aplica una restricción de tipo, se produce una excepción de coincidencia ambigua.

El atributo [Consumes] se aplica a ambas acciones. La acción PostJson controla las solicitudes enviadas con un encabezado Content-Type de application/json. La acción PostForm controla las solicitudes enviadas con un encabezado Content-Type de application/x-www-form-urlencoded.

Recursos adicionales

ASP.NET Core admite la creación de servicios RESTful, lo que también se conoce como API web, mediante C#. Para gestionar las solicitudes, una API web usa controladores. Los controladores de una API web son clases que se derivan de ControllerBase. En este artículo se muestra cómo usar controladores para gestionar las solicitudes de API web.

Vea o descargue el código de ejemplo. (Método de descarga).

Clase ControllerBase

Una API web consta de una o varias clases de controlador que se derivan de ControllerBase. La plantilla de proyecto de API web proporciona un controlador de inicio:

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

No cree un controlador de API web mediante la derivación de la clase Controller. Controller se deriva de ControllerBase y agrega compatibilidad con vistas, por lo que sirve para gestionar páginas web, no solicitudes de API web. Hay una excepción a esta regla: si tiene pensado usar el mismo controlador tanto para las vistas como para las API web, debe derivarlo de Controller.

La clase ControllerBase ofrece muchas propiedades y métodos que son útiles para gestionar solicitudes HTTP. Por ejemplo, ControllerBase.CreatedAtAction devuelve un código de estado 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 continuación se muestran algunos ejemplos más de métodos que proporciona ControllerBase.

Método Notas
BadRequest Devuelve el código de estado 400.
NotFound Devuelve el código de estado 404.
PhysicalFile Devuelve un archivo.
TryUpdateModelAsync Invoca el enlace de modelo.
TryValidateModel Invoca la validación de modelos.

Para ver una lista de todos los métodos y propiedades disponibles, consulte ControllerBase.

Atributos

El espacio de nombres Microsoft.AspNetCore.Mvc proporciona atributos que se pueden usar para configurar el comportamiento de los controladores API web y los métodos de acción. En el ejemplo siguiente se usan atributos para especificar el verbo de acción HTTP admitido y cualquier código de estado HTTP conocido que se pueda devolver:

[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);
}

Estos son algunos ejemplos más de atributos que están disponibles.

Atributo Notas
[Route] Especifica el patrón de dirección URL de un controlador o una acción.
[Bind] Especifica el prefijo y las propiedades que se incluirán en el enlace de modelo.
[HttpGet] Identifica una acción que admite el verbo de acción GET HTTP.
[Consumes] Especifica los tipos de datos que acepta una acción.
[Produces] Especifica los tipos de datos que devuelve una acción.

Para ver una lista que incluye los atributos disponibles, consulte el espacio de nombres Microsoft.AspNetCore.Mvc.

Atributo ApiController

El atributo [ApiController] se puede aplicar a una clase de controlador para permitir los siguientes comportamientos específicos de la API:

Atributo en controladores específicos

El atributo [ApiController] puede aplicarse a controladores específicos, como se muestra en el siguiente ejemplo de la plantilla de proyecto:

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

Atributo en varios controladores

Una estrategia para el uso del atributo en más de un controlador consiste en crear una clase personalizada de controlador base anotada con el atributo [ApiController]. En el siguiente ejemplo se muestra una clase base personalizada y un controlador que se deriva de ella:

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

Atributo en un ensamblado

El atributo [ApiController] puede aplicarse a un ensamblado. En este contexto, la anotación aplica el comportamiento de API web a todos los controladores del ensamblado. No hay ninguna manera de excluir controladores específicos. Aplique el atributo de nivel de ensamblado a la declaración del espacio de nombres de alrededor de la clase Startup:

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

Requisito de enrutamiento mediante atributos

El atributo [ApiController] convierte el enrutamiento de atributos en un requisito. Por ejemplo:

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

No es posible acceder a las acciones mediante rutas convencionales que hayan definido UseEndpoints, UseMvc o UseMvcWithDefaultRoute en Startup.Configure.

Respuestas HTTP 400 automáticas

El atributo [ApiController] hace que los errores de validación de un modelo desencadenen automáticamente una respuesta HTTP 400. Por lo tanto, el siguiente código no es necesario en un método de acción:

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

ASP.NET Core MVC usa el filtro de acciones ModelStateInvalidFilter para realizar la comprobación anterior.

Respuesta BadRequest predeterminada

El siguiente cuerpo de solicitud es un ejemplo del tipo serializado:

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

El tipo de respuesta predeterminado para una respuesta HTTP 400 es ValidationProblemDetails. El siguiente cuerpo de solicitud es un ejemplo del 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."
    ]
  }
}

Tipo ValidationProblemDetails:

  • Proporciona un formato de lectura mecánica para especificar errores en las respuestas de la API web.
  • Cumple los requisitos de la especificación RFC 7807.

Para que las respuestas automáticas y personalizadas sean coherentes, llame al método ValidationProblem en lugar de a BadRequest. ValidationProblem devuelve un objeto ValidationProblemDetails y la respuesta automática.

Registro de respuestas 400 automáticas

Para registrar respuestas 400 automáticas, establezca la propiedad delegada InvalidModelStateResponseFactory para realizar el procesamiento personalizado en Startup.ConfigureServices. De forma predeterminada, InvalidModelStateResponseFactory utiliza ProblemDetailsFactory para crear una instancia de ValidationProblemDetails.

En el ejemplo siguiente se muestra cómo recuperar una instancia de ILogger<TCategoryName> para registrar información sobre una respuesta 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);
        };
    });

Deshabilitación de las respuestas 400 automáticas

Para deshabilitar el comportamiento 400 automático, establezca la propiedad SuppressModelStateInvalidFilter en true. Agregue el código resaltado siguiente en 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;
    });

Inferencia de parámetro de origen de enlace

Un atributo de origen de enlace define la ubicación del valor del parámetro de una acción. Existen los atributos de origen de enlace siguientes:

Atributo Origen de enlace
[FromBody] Cuerpo de la solicitud
[FromForm] Datos del formulario en el cuerpo de la solicitud
[FromHeader] Encabezado de la solicitud
[FromQuery] Parámetro de la cadena de consulta de la solicitud
[FromRoute] Datos de ruta de la solicitud actual
[FromServices] Servicio de solicitud insertado como parámetro de acción

Advertencia

No use [FromRoute] si los valores pueden contener %2f (es decir, /). %2f no incluirá el carácter sin escape /. Use [FromQuery] si el valor puede contener %2f.

Sin el atributo [ApiController] o los atributos de origen de enlace, como [FromQuery], el entorno de tiempo de ejecución de ASP.NET Core intenta usar el enlazador de modelos de objetos complejos. El enlazador de modelos de objetos complejos extrae los datos de los proveedores de valor en un orden definido.

En el ejemplo siguiente, el atributo [FromQuery] indica que el valor del parámetro discontinuedOnly se proporciona en la cadena de consulta de la dirección URL de la solicitud:

[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;
}

El atributo [ApiController] aplica reglas de inferencia a los orígenes de datos predeterminados de los parámetros de acción. Estas reglas aplican atributos a los parámetros de acción, lo que ahorra la necesidad de identificar los orígenes de enlace manualmente. Las reglas de inferencia de orígenes de enlace se comportan de la manera siguiente:

  • [FromBody] se infiere para parámetros de tipo complejo. La excepción a [FromBody] esta regla es cualquier tipo integrado complejo que tenga un significado especial, como IFormCollection y CancellationToken. El código de inferencia del origen de enlace omite esos tipos especiales.
  • [FromForm] se infiere para los parámetros de acción de tipo IFormFile y IFormFileCollection. No se infiere para los tipos simples o definidos por el usuario.
  • [FromRoute] se infiere para cualquier nombre de parámetro de acción que coincida con un parámetro de la plantilla de ruta. Si varias rutas coinciden con un parámetro de acción, cualquier valor de ruta se considera [FromRoute].
  • [FromQuery] se infiere para cualquier otro parámetro de acción.

Notas de la inferencia de FromBody

En el caso de los tipos simples, como string y int, [FromBody] no se infiere. Así pues, para los tipos simples, en los casos en los que quiera utilizar dicha funcionalidad, conviene usar el atributo [FromBody].

Cuando una acción tiene más de un parámetro enlazado desde el cuerpo de solicitud, se produce una excepción. Por ejemplo, todas las firmas de acción siguientes provocan una excepción:

  • [FromBody] se infiere en ambos porque son tipos complejos.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • El atributo [FromBody] en uno se infiere en el otro porque es un tipo complejo.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • El atributo [FromBody] se infiere en ambos.

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

Deshabilitación de las reglas de inferencia

Para deshabilitar la inferencia del origen de enlace, establezca SuppressInferBindingSourcesForParameters en true. Agregue el siguiente código en 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;
    });

Inferencia de solicitud de varios elementos o datos de formulario

El atributo [ApiController] aplica una regla de inferencia para los parámetros de acción de tipo IFormFile y IFormFileCollection. El tipo de contenido de la solicitud multipart/form-data se infiere para estos tipos.

Para deshabilitar el comportamiento predeterminado, establezca la propiedad SuppressConsumesConstraintForFormFileParameters en true en 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;
    });

Detalles de problemas de los códigos de estado de error

MVC transforma un resultado de error (un resultado con un código de estado 400 o superior) en un resultado con ProblemDetails. El tipo ProblemDetails se basa en la especificación RFC 7807 para proporcionar detalles de error de lectura mecánica en una respuesta HTTP.

Observe el código siguiente en una acción de controlador:

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

El método NotFound genera un código de estado HTTP 404 con un cuerpo ProblemDetails. Por ejemplo:

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

Deshabilitación de la respuesta ProblemDetails

La creación automática de ProblemDetails para los códigos de estado de error está deshabilitada cuando la propiedad SuppressMapClientErrors está establecida en true. Agregue el siguiente código en 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;
    });

Definición de tipos de contenido de la solicitud compatibles con el atributo [Consumes]

De forma predeterminada, una acción admite todos los tipos de contenido de la solicitud disponibles. Por ejemplo, si una aplicación está configurada para admitir formateadores de entradaJSON y XML, una acción admite varios tipos de contenido, incluidos application/json y application/xml.

El atributo [Consumes] permite que una acción limite los tipos de contenido de la solicitud compatibles. Aplique el atributo [Consumes] a una acción o controlador, especificando uno o varios tipos de contenido:

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

En el código anterior, la acción CreateProduct especifica el tipo de contenido application/xml. Las solicitudes enrutadas a esta acción deben especificar un encabezado Content-Type de application/xml. Las solicitudes que no especifican un encabezado Content-Type de application/xml generan una respuesta 415 Tipo de medio no compatible.

El atributo [Consumes] también permite que una acción influya en su selección en función del tipo de contenido de una solicitud entrante aplicando una restricción de tipo. Considere el ejemplo siguiente:

[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 });
}

En el código anterior, ConsumesController se configura para controlar las solicitudes enviadas a la dirección URL https://localhost:5001/api/Consumes. Las dos acciones del controlador, PostJson y PostForm, controlan las solicitudes POST con la misma dirección URL. Sin el atributo [Consumes] que aplica una restricción de tipo, se produce una excepción de coincidencia ambigua.

El atributo [Consumes] se aplica a ambas acciones. La acción PostJson controla las solicitudes enviadas con un encabezado Content-Type de application/json. La acción PostForm controla las solicitudes enviadas con un encabezado Content-Type de application/x-www-form-urlencoded.

Recursos adicionales

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

No cree un controlador de API web mediante la derivación de la clase Controller. Controller se deriva de ControllerBase y agrega compatibilidad con vistas, por lo que sirve para gestionar páginas web, no solicitudes de API web. Hay una excepción a esta regla: si tiene pensado usar el mismo controlador tanto para las vistas como para las API web, debe derivarlo de Controller. La clase ControllerBase ofrece muchas propiedades y métodos que son útiles para gestionar solicitudes HTTP. Por ejemplo, ControllerBase.CreatedAtAction devuelve un código de estado 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 continuación se muestran algunos ejemplos más de métodos que proporciona ControllerBase:

Método Notas
BadRequest Devuelve el código de estado 400.
NotFound Devuelve el código de estado 404.
PhysicalFile Devuelve un archivo.
TryUpdateModelAsync Invoca el enlace de modelo.
TryValidateModel Invoca la validación de modelos.

Para ver una lista de todos los métodos y propiedades disponibles, consulte ControllerBase.

Atributos

El espacio de nombres Microsoft.AspNetCore.Mvc proporciona atributos que se pueden usar para configurar el comportamiento de los controladores API web y los métodos de acción. En el ejemplo siguiente se usan atributos para especificar el verbo de acción HTTP admitido y cualquier código de estado HTTP conocido que se pueda devolver:

[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);
}

Estos son algunos ejemplos más de atributos que están disponibles:

Atributo Notas
[Route] Especifica el patrón de dirección URL de un controlador o una acción.
[Bind] Especifica el prefijo y las propiedades que se incluirán en el enlace de modelo.
[HttpGet] Identifica una acción que admite el verbo de acción GET HTTP.
[Consumes] Especifica los tipos de datos que acepta una acción.
[Produces] Especifica los tipos de datos que devuelve una acción.

Para ver una lista que incluye los atributos disponibles, consulte el espacio de nombres Microsoft.AspNetCore.Mvc.

Atributo ApiController

El atributo [ApiController] se puede aplicar a una clase de controlador para permitir los siguientes comportamientos específicos de la API:

Atributo en controladores específicos

El atributo [ApiController] puede aplicarse a controladores específicos, como se muestra en el siguiente ejemplo de la plantilla de proyecto:

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

Atributo en varios controladores

Una estrategia para el uso del atributo en más de un controlador consiste en crear una clase personalizada de controlador base anotada con el atributo [ApiController]. En el siguiente ejemplo se muestra una clase base personalizada y un controlador que se deriva de ella:

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

Atributo en un ensamblado

Si la versión de compatibilidad está establecida en 2.2 o posterior, el atributo [ApiController] se puede aplicar a un ensamblado. En este contexto, la anotación aplica el comportamiento de API web a todos los controladores del ensamblado. No hay ninguna manera de excluir controladores específicos. Aplique el atributo de nivel de ensamblado a la declaración del espacio de nombres de alrededor de la clase Startup:

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

Requisito de enrutamiento mediante atributos

El atributo [ApiController] convierte el enrutamiento de atributos en un requisito. Por ejemplo:

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

Las acciones no son accesibles mediante rutas convencionales definidas por UseMvc o UseMvcWithDefaultRoute en Startup.Configure.

Respuestas HTTP 400 automáticas

El atributo [ApiController] hace que los errores de validación de un modelo desencadenen automáticamente una respuesta HTTP 400. Por lo tanto, el siguiente código no es necesario en un método de acción:

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

ASP.NET Core MVC usa el filtro de acciones ModelStateInvalidFilter para realizar la comprobación anterior.

Respuesta BadRequest predeterminada

Con una versión de compatibilidad de 2.1, el tipo de respuesta predeterminado para una respuesta HTTP 400 es SerializableError. El siguiente cuerpo de solicitud es un ejemplo del tipo serializado:

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

Con una versión de compatibilidad de 2.2 o posterior, el tipo de respuesta predeterminado para una respuesta HTTP 400 es ValidationProblemDetails. El siguiente cuerpo de solicitud es un ejemplo del 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."
    ]
  }
}

Tipo ValidationProblemDetails:

  • Proporciona un formato de lectura mecánica para especificar errores en las respuestas de la API web.
  • Cumple los requisitos de la especificación RFC 7807.

Para que las respuestas automáticas y personalizadas sean coherentes, llame al método ValidationProblem en lugar de a BadRequest. ValidationProblem devuelve un objeto ValidationProblemDetails y la respuesta automática.

Registro de respuestas 400 automáticas

Vea Cómo registrar respuestas 400 automáticas sobre errores de validación de modelos (aspnet/AspNetCore.Docs #12157).

Deshabilitación de las respuestas 400 automáticas

Para deshabilitar el comportamiento 400 automático, establezca la propiedad SuppressModelStateInvalidFilter en true. Agregue el código resaltado siguiente en Startup.ConfigureServices:

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

Inferencia de parámetro de origen de enlace

Un atributo de origen de enlace define la ubicación del valor del parámetro de una acción. Existen los atributos de origen de enlace siguientes:

Atributo Origen de enlace
[FromBody] Cuerpo de la solicitud
[FromForm] Datos del formulario en el cuerpo de la solicitud
[FromHeader] Encabezado de la solicitud
[FromQuery] Parámetro de la cadena de consulta de la solicitud
[FromRoute] Datos de ruta de la solicitud actual
[FromServices] Servicio de solicitud insertado como parámetro de acción

Advertencia

No use [FromRoute] si los valores pueden contener %2f (es decir, /). %2f no incluirá el carácter sin escape /. Use [FromQuery] si el valor puede contener %2f. Sin el atributo [ApiController] o los atributos de origen de enlace, como [FromQuery], el entorno de tiempo de ejecución de ASP.NET Core intenta usar el enlazador de modelos de objetos complejos. El enlazador de modelos de objetos complejos extrae los datos de los proveedores de valor en un orden definido.

En el ejemplo siguiente, el atributo [FromQuery] indica que el valor del parámetro discontinuedOnly se proporciona en la cadena de consulta de la dirección URL de la solicitud:

[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;
}

El atributo [ApiController] aplica reglas de inferencia a los orígenes de datos predeterminados de los parámetros de acción. Estas reglas aplican atributos a los parámetros de acción, lo que ahorra la necesidad de identificar los orígenes de enlace manualmente. Las reglas de inferencia de orígenes de enlace se comportan de la manera siguiente:

  • [FromBody] se infiere para parámetros de tipo complejo. La excepción a [FromBody] esta regla es cualquier tipo integrado complejo que tenga un significado especial, como IFormCollection y CancellationToken. El código de inferencia del origen de enlace omite esos tipos especiales.
  • [FromForm] se infiere para los parámetros de acción de tipo IFormFile y IFormFileCollection. No se infiere para los tipos simples o definidos por el usuario.
  • [FromRoute] se infiere para cualquier nombre de parámetro de acción que coincida con un parámetro de la plantilla de ruta. Si varias rutas coinciden con un parámetro de acción, cualquier valor de ruta se considera [FromRoute].
  • [FromQuery] se infiere para cualquier otro parámetro de acción.

Notas de la inferencia de FromBody

En el caso de los tipos simples, como string y int, [FromBody] no se infiere. Así pues, para los tipos simples, en los casos en los que quiera utilizar dicha funcionalidad, conviene usar el atributo [FromBody].

Cuando una acción tiene más de un parámetro enlazado desde el cuerpo de solicitud, se produce una excepción. Por ejemplo, todas las firmas de acción siguientes provocan una excepción:

  • [FromBody] se infiere en ambos porque son tipos complejos.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • El atributo [FromBody] en uno se infiere en el otro porque es un tipo complejo.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • El atributo [FromBody] se infiere en ambos.

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

Nota

En ASP.NET Core 2.1, los parámetros de tipo de colección, como listas y matrices, se infieren incorrectamente como [FromQuery]. El atributo [FromBody] debe usarse con estos parámetros si van a enlazarse desde el cuerpo de solicitud. Este comportamiento se ha corregido en ASP.NET Core 2.2 o posterior, donde se infieren los parámetros de tipo de colección para enlazarse desde el cuerpo de forma predeterminada.

Deshabilitación de las reglas de inferencia

Para deshabilitar la inferencia del origen de enlace, establezca SuppressInferBindingSourcesForParameters en true. Agregue el siguiente código en Startup.ConfigureServices:

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

Inferencia de solicitud de varios elementos o datos de formulario

El atributo [ApiController] aplica una regla de inferencia para los parámetros de acción de tipo IFormFile y IFormFileCollection. El tipo de contenido de la solicitud multipart/form-data se infiere para estos tipos. Para deshabilitar el comportamiento predeterminado, establezca la propiedad SuppressConsumesConstraintForFormFileParameters en true en Startup.ConfigureServices:

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

Detalles de problemas de los códigos de estado de error

Cuando la versión de compatibilidad es 2.2 o posterior, MVC transforma un resultado de error (un resultado con el código de estado 400 o superior) en un resultado con ProblemDetails. El tipo ProblemDetails se basa en la especificación RFC 7807 para proporcionar detalles de error de lectura mecánica en una respuesta HTTP. Observe el código siguiente en una acción de controlador:

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

El método NotFound genera un código de estado HTTP 404 con un cuerpo ProblemDetails. Por ejemplo:

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

Deshabilitación de la respuesta ProblemDetails

La creación automática de ProblemDetails para los códigos de estado de error está deshabilitada cuando la propiedad SuppressMapClientErrors está establecida en true. Agregue el siguiente código en Startup.ConfigureServices:

Definición de tipos de contenido de la solicitud compatibles con el atributo [Consumes]

De forma predeterminada, una acción admite todos los tipos de contenido de la solicitud disponibles. Por ejemplo, si una aplicación está configurada para admitir formateadores de entradaJSON y XML, una acción admite varios tipos de contenido, incluidos application/json y application/xml.

El atributo [Consumes] permite que una acción limite los tipos de contenido de la solicitud compatibles. Aplique el atributo [Consumes] a una acción o controlador, especificando uno o varios tipos de contenido:

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

En el código anterior, la acción CreateProduct especifica el tipo de contenido application/xml. Las solicitudes enrutadas a esta acción deben especificar un encabezado Content-Type de application/xml. Las solicitudes que no especifican un encabezado Content-Type de application/xml generan una respuesta 415 Tipo de medio no compatible. El atributo [Consumes] también permite que una acción influya en su selección en función del tipo de contenido de una solicitud entrante aplicando una restricción de tipo. Considere el ejemplo siguiente:

[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 });
}

En el código anterior, ConsumesController se configura para controlar las solicitudes enviadas a la dirección URL https://localhost:5001/api/Consumes. Las dos acciones del controlador, PostJson y PostForm, controlan las solicitudes POST con la misma dirección URL. Sin el atributo [Consumes] que aplica una restricción de tipo, se produce una excepción de coincidencia ambigua. El atributo [Consumes] se aplica a ambas acciones. La acción PostJson controla las solicitudes enviadas con un encabezado Content-Type de application/json. La acción PostForm controla las solicitudes enviadas con un encabezado Content-Type de application/x-www-form-urlencoded.

Recursos adicionales