Aplicación de formato a datos de respuesta en ASP.NET Core Web API

Por Rick Anderson y Steve Smith

ASP.NET Core MVC tiene compatibilidad para formatear datos de respuesta. Se pueden formatear los datos de respuesta con formatos específicos o en respuesta al formato solicitado por el cliente.

Vea o descargue el código de ejemplo (cómo descargarlo)

Resultados de acción específicos del formato

Algunos tipos de resultado de acción son específicos de un formato determinado, como JsonResult y ContentResult. Las acciones pueden devolver resultados con un formato determinado, independientemente de las preferencias del cliente. Por ejemplo, la devolución de JsonResult devuelve datos con formato JSON. Al devolver ContentResult o una cadena se devuelven datos de cadena con formato de texto sin formato.

No se requiere una acción para devolver ningún tipo específico. ASP.NET Core admite cualquier valor devuelto de objeto. Los resultados de acciones que devuelven objetos que no son tipos IActionResult se serializan con la implementación IOutputFormatter correspondiente. Para obtener más información, vea Tipos de valor devuelto de acción del controlador de la API web de ASP.NET Core.

El método auxiliar integrado Ok devuelve datos con formato JSON: [!code-csharp]

La descarga de ejemplo devuelve la lista de autores. Con las herramientas para desarrolladores del explorador F12 o Postman con el código anterior:

  • Se muestra el encabezado de respuesta que contiene content-type: application/json; charset=utf-8.
  • Se muestran los encabezados de solicitud. Por ejemplo, el encabezado Accept. El código anterior omite el encabezado Accept.

Para devolver datos con formato de texto sin formato, use ContentResult y el método del asistente Content:

// GET api/authors/about
[HttpGet("About")]
public ContentResult About()
{
    return Content("An API listing authors of docs.asp.net.");
}

En el código anterior, el Content-Type devuelto es text/plain. Al devolver una cadena se proporciona Content-Type de text/plain:

// GET api/authors/version
[HttpGet("version")]
public string Version()
{
    return "Version 1.0.0";
}

Para las acciones con varios tipos de valor devuelto, devuelva IActionResult. Por ejemplo, la devolución de códigos de estado HTTP diferentes en función del resultado de las operaciones realizadas.

Negociación de contenido

La negociación de contenido se produce cuando el cliente especifica un encabezado Accept. El formato predeterminado que ASP.NET Core usa es JSON. La negociación de contenido:

  • La implementa ObjectResult.
  • Se integra en los resultados de acción específicos del código de estado devueltos por los métodos auxiliares. Los métodos auxiliares de los resultados de acción se basan en ObjectResult.

Cuando se devuelve un tipo de modelo, el tipo de valor devuelto es ObjectResult.

El siguiente método de acción usa los métodos del asistente Ok y NotFound:

// GET: api/authors/search?namelike=th
[HttpGet("Search")]
public IActionResult Search(string namelike)
{
    var result = _authors.GetByNameSubstring(namelike);
    if (!result.Any())
    {
        return NotFound(namelike);
    }
    return Ok(result);
}

De forma predeterminada, ASP.NET Core admite los tipos de medios application/json, text/json y text/plain. Las herramientas como Fiddler o Postman pueden establecer el encabezado de solicitud Accept para especificar el formato de devolución. Cuando el encabezado Accept contiene un tipo que el servidor admite, se devuelve ese tipo. En la sección siguiente se muestra cómo agregar otros formateadores.

Las acciones del controlador pueden devolver POCO (objetos CLR antiguos sin formato). Cuando se devuelve un objeto POCO, el tiempo de ejecución crea automáticamente un ObjectResult que encapsula al objeto. El cliente obtiene el objeto serializado con formato. Si el objeto que se va a devolver es null, se devuelve una respuesta 204 No Content.

Devolución de un tipo de objeto:

// GET api/authors/RickAndMSFT
[HttpGet("{alias}")]
public Author Get(string alias)
{
    return _authors.GetByAlias(alias);
}

En el código anterior, una solicitud de un alias de autor válido devuelve una respuesta 200 OK con los datos del autor. Una solicitud de un alias no válido devuelve una respuesta 204 No Content.

El encabezado Accept

La negociación de contenido se lleva a cabo cuando en la solicitud aparece un encabezado Accept. Cuando una solicitud contiene un encabezado Accept, ASP.NET Core:

  • Enumera los tipos de medios del encabezado Accept en orden de preferencia.
  • Intenta encontrar un formateador que pueda generar una respuesta en uno de los formatos especificados.

Si no se encuentra ningún formateador que pueda satisfacer la solicitud del cliente, ASP.NET Core:

  • Devuelve 406 Not Acceptable si está establecido en o MvcOptions.ReturnHttpNotAcceptable true -
  • Intenta encontrar el primer formateador que puede generar una respuesta.

Si no se ha configurado ningún formateador para el formato solicitado, se usará el primer formateador que pueda dar formato al objeto. Si no aparece ningún encabezado Accept en la solicitud:

  • El primer formateador que puede controlar el objeto se usa para serializar la respuesta.
  • No tiene lugar ninguna negociación. El servidor está determinando el formato que se va a devolver.

Si el encabezado Accept contiene */*, el encabezado se omite a menos que RespectBrowserAcceptHeader esté establecido en true en MvcOptions.

Exploradores y negociación de contenido

A diferencia de los clientes de API típicos, los exploradores web proporcionan encabezados Accept. El explorador web especifica muchos formatos, incluidos los comodines. De forma predeterminada, cuando el marco detecta que la solicitud procede de un explorador:

  • El encabezado Accept se omite.
  • El contenido se devuelve en JSON, a menos que se configure de otra manera.

Esto proporciona una experiencia más coherente entre los exploradores al consumir las API.

Para configurar una aplicación para que respete los encabezados de aceptación del explorador, establezca RespectBrowserAcceptHeader en true:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.RespectBrowserAcceptHeader = true; // false by default
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.RespectBrowserAcceptHeader = true; // false by default
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Configuración de formateadores

Las aplicaciones que necesitan admitir formatos adicionales pueden agregar los paquetes NuGet adecuados y configurar la compatibilidad. Hay formateadores independientes para la entrada y la salida. El enlace de modelos usa formateadores de entrada. Los formateadores de salida se usan para dar formato a las respuestas. Para obtener información sobre la creación de un formateador personalizado, vea Formateadores personalizados.

Adición de compatibilidad con el formato XML

Los formateadores XML que se implementan mediante XmlSerializer se configuran llamando a AddXmlSerializerFormatters:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddXmlSerializerFormatters();
}

El código anterior serializa los resultados mediante XmlSerializer.

Cuando se usa el código anterior, los métodos de controlador devuelven el formato adecuado en función del encabezado Accept de la solicitud.

Configuración System.Text.Json de formateadores basados

Las características de System.Text.Json los formateadores basados se pueden configurar mediante Microsoft.AspNetCore.Mvc.JsonOptions.JsonSerializerOptions . El formato predeterminado es camelCase. El código resaltado siguiente establece el formato PascalCase:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
            .AddJsonOptions(options => 
               options.JsonSerializerOptions.PropertyNamingPolicy = null);
}

El siguiente método de acción llama a ControllerBase.Problem para crear una ProblemDetails respuesta:

[HttpGet("error")]
public IActionResult GetError()
{
    return Problem("Something went wrong!");
}

Con el código anterior:

  • https://localhost:5001/WeatherForecast/temperature devuelve PascalCase.
  • https://localhost:5001/WeatherForecast/error devuelve camelCase. La respuesta de error siempre es camelCase, incluso cuando la aplicación establece el formato en PascalCase. ProblemDetails sigue a RFC 7807,que especifica minúsculas

El código siguiente establece PascalCase y agrega un convertidor personalizado:

services.AddControllers().AddJsonOptions(options =>
{
    // Use the default property (Pascal) casing.
    options.JsonSerializerOptions.PropertyNamingPolicy = null;

    // Configure a custom converter.
    options.JsonSerializerOptions.Converters.Add(new MyCustomJsonConverter());
});

Las opciones de serialización de salida se pueden configurar para cada acción mediante JsonResult. Por ejemplo:

public IActionResult Get()
{
    return Json(model, new JsonSerializerOptions
    {
        WriteIndented = true,
    });
}

Adición de compatibilidad con el formato JSON basado en Newtonsoft.Json

Antes de ASP.NET Core 3.0, los formateadores JSON usados de forma predeterminada son los implementados mediante el paquete Newtonsoft.Json. En ASP.NET Core 3.0 o posterior, los formateadores JSON predeterminados se basan en System.Text.Json. La compatibilidad con formateadores y características basados está disponible instalando el Newtonsoft.Json Microsoft.AspNetCore.Mvc.NewtonsoftJson paquete NuGet y configurándose en Startup.ConfigureServices .

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddNewtonsoftJson();
}

En el código anterior, la llamada a configura las siguientes características de AddNewtonsoftJson Web API, MVC y Razor Pages para usar Newtonsoft.Json :

Es posible que algunas características no funcionen bien con formateadores basados en System.Text.Json y requieren una referencia a los formateadores basados en Newtonsoft.Json. Siga usando los formateadores basados en Newtonsoft.Json si la aplicación:

  • Usa atributos Newtonsoft.Json. Por ejemplo, [JsonProperty] o [JsonIgnore].
  • Proporciona la configuración de la serialización.
  • Se basa en las características que Newtonsoft.Json proporciona.
  • Configura Microsoft.AspNetCore.Mvc.JsonResult.SerializerSettings. Antes de ASP.NET Core 3.0, JsonResult.SerializerSettings acepta una instancia de JsonSerializerSettings específica de Newtonsoft.Json.

Las características para los formateadores basados en Newtonsoft.Json pueden configurarse mediante Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.SerializerSettings:

services.AddControllers().AddNewtonsoftJson(options =>
{
    // Use the default property (Pascal) casing
    options.SerializerSettings.ContractResolver = new DefaultContractResolver();

    // Configure a custom converter
    options.SerializerSettings.Converters.Add(new MyCustomJsonConverter());
});

Las opciones de serialización de salida se pueden configurar para cada acción mediante JsonResult. Por ejemplo:

public IActionResult Get()
{
    return Json(model, new JsonSerializerSettings
    {
        Formatting = Formatting.Indented,
    });
}

Adición de compatibilidad con el formato XML

El formato XML requiere el paquete NuGet Microsoft.AspNetCore.Mvc.Formatters.Xml.

Los formateadores XML que se implementan mediante XmlSerializer se configuran llamando a AddXmlSerializerFormatters:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddXmlSerializerFormatters();
}

El código anterior serializa los resultados mediante XmlSerializer.

Cuando se usa el código anterior, los métodos de controlador deben devolver el formato adecuado en función del encabezado Accept de la solicitud.

Especificación de un formato

Para restringir los formatos de respuesta, aplique el [Produces] filtro. Al igual que la mayoría delos [Produces] filtros, se puede aplicar en la acción, el controlador o el ámbito global:

[ApiController]
[Route("[controller]")]
[Produces("application/json")]
public class WeatherForecastController : ControllerBase
{

El filtro [Produces] anterior:

  • Obliga a que todas las acciones dentro del controlador devuelvan respuestas con formato JSON.
  • Si se configuran otros formateadores y el cliente especifica un formato diferente, se devuelve JSON.

Para más información, consulte Filtros.

Formateadores de casos especiales

Algunos casos especiales se implementan mediante formateadores integrados. De forma predeterminada, los tipos de valor devueltos string se formatean como texto/sin formato (texto/html si se solicita a través del encabezado Accept). Este comportamiento se puede quitar mediante la eliminación de StringOutputFormatter. Los formateadores se quitan en el método ConfigureServices. Las acciones que tienen un tipo de valor devuelto de objeto de modelo devuelven 204 No Content al devolver null. Este comportamiento se puede quitar mediante la eliminación de HttpNoContentOutputFormatter. El código siguiente quita StringOutputFormatter y HttpNoContentOutputFormatter.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        // requires using Microsoft.AspNetCore.Mvc.Formatters;
        options.OutputFormatters.RemoveType<StringOutputFormatter>();
        options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        // requires using Microsoft.AspNetCore.Mvc.Formatters;
        options.OutputFormatters.RemoveType<StringOutputFormatter>();
        options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Sin StringOutputFormatter, el formateador de JSON integrado aplica formato a los tipos devueltos string. Si se quita el formateador JSON integrado y está disponible un formateador XML, el formateador XML aplica formato a los tipos devueltos string. De lo contrario, los tipos devueltos string devuelven 406 Not Acceptable.

Sin HttpNoContentOutputFormatter, se da formato a los objetos nulos mediante el formateador configurado. Por ejemplo:

  • El formateador JSON devuelve una respuesta con un cuerpo de null.
  • El formateador XML devuelve un elemento XML vacío con el atributo xsi:nil="true" establecido.

Asignaciones de direcciones URL de formato de respuesta

Los clientes pueden solicitar un formato determinado como parte de la dirección URL, por ejemplo:

  • En la cadena de consulta o en una parte de la ruta de acceso.
  • Mediante el uso de una extensión de archivo específica del formato como .xml o .json.

La asignación de la ruta de acceso de la solicitud debe especificarse en la ruta que use la API. Por ejemplo:

[Route("api/[controller]")]
[ApiController]
[FormatFilter]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}.{format?}")]
    public Product Get(int id)
    {

Esta ruta anterior permite especificar el formato solicitado como una extensión de archivo opcional. El atributo comprueba la existencia del valor de formato en y asigna el formato de respuesta al formateador adecuado [FormatFilter] cuando se crea la RouteData respuesta.

Ruta Formateador
/api/products/5 Formateador de salida predeterminado
/api/products/5.json Formateador JSON (si está configurado)
/api/products/5.xml Formateador XML (si está configurado)