Estoy buscando la forma correcta de devolver JSON con un código de estado HTTP en mi controlador de la API web de .NET Core. Yo uso para utilizarlo como este:
public IHttpActionResult GetResourceData()
{
return this.Content(HttpStatusCode.OK, new { response = "Hello"});
}
Esto era en una aplicación MVC 4.6 pero ahora con .NET Core parece que no tengo este IHttpActionResult
tengo ActionResult
y lo uso así:
public ActionResult IsAuthenticated()
{
return Ok(Json("123"));
}
Pero la respuesta del servidor es rara, como en la imagen de abajo:
[]
Sólo quiero que el controlador de la Web API devuelva JSON con un código de estado HTTP como hice en la Web API 2.
La versión más básica que responde con un JsonResult
es:
// GET: api/authors
[HttpGet]
public JsonResult Get()
{
return Json(_authorRepository.List());
}
Sin embargo, esto no va a ayudar con su problema porque no puede tratar explícitamente su propio código de respuesta.
La forma de controlar los resultados de estado es devolver un ActionResult
que es donde se puede aprovechar el tipo StatusCodeResult
.
por ejemplo:
// GET: api/authors/search?namelike=foo
[HttpGet("Search")]
public IActionResult Search(string namelike)
{
var result = _authorRepository.GetByNameSubstring(namelike);
if (!result.Any())
{
return NotFound(namelike);
}
return Ok(result);
}
Tenga en cuenta que los dos ejemplos anteriores proceden de una magnífica guía disponible en Microsoft Documentation: Formato de datos de respuesta
El problema que me encuentro a menudo es que quería un control más granular sobre mi WebAPI en lugar de ir con la configuración por defecto de la "Nuevo Proyecto" plantilla en VS.
Vamos a asegurarnos de que tiene algunos de los fundamentos hacia abajo ...
Para conseguir que tu ASP.NET Core WebAPI responda con un Objeto Serializado JSON junto con el control total del código de estado, deberías empezar asegurándote de que has incluido el servicio AddMvc()
en tu método ConfigureServices
que normalmente se encuentra en Startup.cs
.
Es importante tener en cuenta que AddMvc()
incluirá automáticamente el formateador de entrada/salida para JSON junto con la respuesta a otros tipos de peticiones.
Si tu proyecto requiere control total y quieres definir estrictamente tus servicios, como por ejemplo cómo se comportará tu WebAPI ante varios tipos de petición, incluyendo application/json
y no responder a otros tipos de petición (como una petición estándar del navegador), puedes definirlo manualmente con el siguiente código:
public void ConfigureServices(IServiceCollection services)
{
// Build a customized MVC implementation, without using the default AddMvc(), instead use AddMvcCore().
// https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc/MvcServiceCollectionExtensions.cs
services
.AddMvcCore(options =>
{
options.RequireHttpsPermanent = true; // does not affect api requests
options.RespectBrowserAcceptHeader = true; // false by default
//options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
//remove these two below, but added so you know where to place them...
options.OutputFormatters.Add(new YourCustomOutputFormatter());
options.InputFormatters.Add(new YourCustomInputFormatter());
})
//.AddApiExplorer()
//.AddAuthorization()
.AddFormatterMappings()
//.AddCacheTagHelper()
//.AddDataAnnotations()
//.AddCors()
.AddJsonFormatters(); // JSON, or you can build your own custom one (above)
}
Notarás que también he incluido una forma de añadir tus propios formateadores de entrada/salida personalizados, en el caso de que quieras responder a otro formato de serialización (protobuf, thrift, etc).
El trozo de código anterior es en su mayor parte un duplicado del método AddMvc()
. Sin embargo, estamos implementando cada servicio "por defecto" por nuestra cuenta mediante la definición de todos y cada uno de los servicios en lugar de ir con el pre-enviado con la plantilla. He añadido el enlace al repositorio en el bloque de código, o puedes consultar AddMvc()
desde el repositorio de GitHub.
Nótese que hay algunas guías que intentarán resolver esto "deshaciendo" los valores por defecto, en lugar de simplemente no implementarlo en primer lugar... Si se tiene en cuenta que ahora estamos trabajando con código abierto, esto es un trabajo redundante, mal código y francamente una vieja costumbre que desaparecerá pronto.
Voy a mostrarte uno muy sencillo para que se resuelva tu duda.
public class FooController
{
[HttpPost]
public async Task<IActionResult> Create([FromBody] Object item)
{
if (item == null) return BadRequest();
var newItem = new Object(); // create the object to return
if (newItem != null) return Ok(newItem);
else return NotFound();
}
}
Content-Type
y Accept
Tienes que asegurarte de que tus cabeceras Content-Type
y Accept
en tu solicitud están configuradas correctamente. En tu caso (JSON), querrás configurarlo como application/json
.
Si quieres que tu WebAPI responda como JSON por defecto, independientemente de lo que especifique la cabecera de la petición, puedes hacerlo de dos maneras.
Forma 1 Como se muestra en el artículo que recomendé antes (Formatting Response Data) puedes forzar un formato particular a nivel de Controlador/Acción. Personalmente no me gusta este enfoque... pero aquí está para completar:
Forzar un formato particular Si quieres restringir los formatos de respuesta para una acción específica puedes, puedes aplicar la opción Filtro [Produces]. El filtro [Produces] especifica los formatos de respuesta formatos de respuesta para una acción específica (o controlador). Como la mayoría de los filtros, este puede aplicarse a la acción, al controlador o al ámbito global.
[Produce("application/json")] public class AutoresControlador
El filtro
[Produces]
forzará todas las acciones dentro de la claseAuthorsController
a devolver respuestas con formato JSON, incluso si se han configurado otros formateadores fueron configurados para la aplicación y el cliente proporcionó un encabezadoAccept
solicitando un formato diferente, disponible.
Método 2 Mi método preferido es que la WebAPI responda a todas las peticiones con el formato solicitado. Sin embargo, en el caso de que no acepte el formato solicitado, entonces regresar** a un formato por defecto (es decir, JSON)
En primer lugar, tendrá que registrar que en sus opciones (tenemos que rehacer el comportamiento por defecto, como se señaló anteriormente)
options.RespectBrowserAcceptHeader = true; // false by default
Por último, simplemente reordenando la lista de los formateadores que se definieron en el constructor de servicios, el host web se ajustará por defecto al formateador que usted coloque en la parte superior de la lista (es decir, la posición 0).
Puede encontrar más información en esta entrada del blog .NET Web Development and Tools
Dispone de métodos predefinidos para los códigos de estado más comunes.
Ok(result)
devuelve 200
con respuestaCreatedAtRoute
devuelve 201
+ la nueva URL del recursoNotFound
devuelve el código 404
.Ver BaseController.cs
y Controller.cs
para una lista de todos los métodos.
Pero si realmente insiste puede usar StatusCode
para establecer un código personalizado, pero realmente no debería hacerlo ya que hace el código menos legible y tendrá que repetir el código para establecer las cabeceras (como para CreatedAtRoute
).
public ActionResult IsAuthenticated()
{
return StatusCode(200, "123");
}
Por favor, refiérase al siguiente código, usted puede manejar múltiples códigos de estado con diferentes tipos de JSON
public async Task<HttpResponseMessage> GetAsync()
{
try
{
using (var entities = new DbEntities())
{
var resourceModelList = entities.Resources.Select(r=> new ResourceModel{Build Your Resource Model}).ToList();
if (resourceModelList.Count == 0)
{
return this.Request.CreateResponse<string>(HttpStatusCode.NotFound, "No resources found.");
}
return this.Request.CreateResponse<List<ResourceModel>>(HttpStatusCode.OK, resourceModelList, "application/json");
}
}
catch (Exception ex)
{
return this.Request.CreateResponse<string>(HttpStatusCode.InternalServerError, "Something went wrong.");
}
}