ASP.NET Core devuelve JSON con código de estado

Estoy buscando la forma correcta de devolver JSON con un código de estado HTTP en mi controlador .NET Core Web API. Yo uso para usarlo así:

public IHttpActionResult GetResourceData() { return this.Content(HttpStatusCode.OK, new { response = "Hello"}); } 

Esto fue en una aplicación 4.6 MVC pero ahora con .NET Core. No parece tener este IHttpActionResult Tengo ActionResult y ActionResult usando esto:

 public ActionResult IsAuthenticated() { return Ok(Json("123")); } 

Pero la respuesta del servidor es extraña, como en la imagen a continuación:

enter image description here

Solo quiero que el controlador de API web devuelva JSON con un código de estado HTTP como lo hice en 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 con su propio código de respuesta.

La forma de obtener el control de los resultados de estado es que debe devolver un ActionResult que es donde puede aprovechar el tipo de 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 ambos ejemplos anteriores provienen de una gran guía disponible de la Documentación de Microsoft: Formateo de datos de respuesta


Material adicional

El problema que encuentro con frecuencia es que quería un control más granular sobre mi WebAPI en lugar de solo ir con la configuración predeterminada de la plantilla “Nuevo proyecto” en VS.

Asegurémonos de tener algunos de los fundamentos abajo …

Paso 1: configura tu servicio

Para que su ASP.NET Core WebAPI responda con un Objeto serializado JSON con control total del código de estado, debe comenzar asegurándose de haber incluido el servicio AddMvc() en su método ConfigureServices que usualmente se encuentra en el 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 solicitud.

Si su proyecto requiere control total y quiere definir estrictamente sus servicios, como por ejemplo cómo se comportará su WebAPI con varios tipos de solicitudes, incluida la application/json y no responde a otros tipos de solicitudes (como una solicitud de navegador estándar), puede definirla 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(); //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 incluí una forma para que agregues tus propios formateadores de entrada / salida personalizados, en caso de que quieras responder a otro formato de serialización (protobuf, ahorro, etc.).

El fragmento de código anterior es principalmente un duplicado del método AddMvc() . Sin embargo, estamos implementando cada servicio “predeterminado” por nuestra propia cuenta definiendo todos y cada uno de los servicios en lugar de ir con la plantilla previamente enviada. He agregado el enlace del repository en el bloque de código, o puede ver AddMvc() desde el repository de GitHub. .

Tenga en cuenta que hay algunas guías que tratarán de resolver esto “deshaciendo” los valores predeterminados, en lugar de no implementarlo en primer lugar … Si tiene en cuenta que ahora estamos trabajando con código abierto, este es un trabajo redundante , código incorrecto y, francamente, un viejo hábito que desaparecerá pronto.


Paso 2: crea un controlador

Te mostraré una muy directa para que tu pregunta sea resuelta.

 public class FooController { [HttpPost] public async Task 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(); } } 

Paso 3: verifica tu tipo de Content-Type y Accept

Debe asegurarse de que sus Content-Type y Accept en su solicitud estén configurados correctamente. En su caso (JSON), querrá configurarlo para que sea application/json .

Si desea que su WebAPI responda como JSON por defecto, independientemente de lo que especifique el encabezado de la solicitud, puede hacerlo de varias maneras .

Modo 1 Como se muestra en el artículo que recomendé anteriormente ( Dar formato a los datos de respuesta ), puede forzar un formato particular en el nivel de Controlador / Acción. Personalmente, no me gusta este enfoque … pero aquí está para completarlo:

Forzar un formato particular Si desea restringir los formatos de respuesta para una acción específica que pueda, puede aplicar el filtro [Produce]. El filtro [Produce] especifica los formatos de respuesta para una acción específica (o controlador). Al igual que la mayoría de los filtros, esto se puede aplicar en la acción, el controlador o el scope global.

 [Produces("application/json")] public class AuthorsController 

El filtro [Produces] obligará a todas las acciones dentro del AuthorsController a devolver respuestas formateadas en JSON, incluso si se configuraron otros formateadores para la aplicación y el cliente proporcionó un encabezado Accept solicitando un formato diferente disponible.

Modo 2 Mi método preferido es que WebAPI responda a todas las solicitudes con el formato solicitado. Sin embargo, en el caso de que no acepte el formato solicitado, se recurrirá a un valor predeterminado (es decir, JSON)

En primer lugar, deberá registrarlo en sus opciones (tenemos que volver a trabajar el comportamiento predeterminado, como se señaló anteriormente)

 options.RespectBrowserAcceptHeader = true; // false by default 

Finalmente, simplemente reordenando la lista de los formateadores que se definieron en el constructor de servicios, el host de web usará de manera predeterminada el formateador que coloque en la parte superior de la lista (es decir, la posición 0).

Se puede encontrar más información en esta entrada del blog .NET de Desarrollo Web y Herramientas

Tiene métodos predefinidos para la mayoría de los códigos de estado comunes.

  • Ok(result) devuelve 200 con respuesta
  • CreatedAtRoute devuelve 201 + nueva URL de recurso
  • NotFound devuelve 404
  • BadRequest devuelve 400 etc.

Consulte BaseController.cs y Controller.cs para obtener una lista de todos los métodos.

Pero si realmente insistes puedes usar StatusCode para establecer un código personalizado, pero no deberías hacerlo ya que hace que el código sea menos legible y tendrás que repetir el código para establecer encabezados (como en el caso de CreatedAtRoute ).

 public ActionResult IsAuthenticated() { return StatusCode(200, Json("123")); } 

Con ASP.NET Core 2.0 , la forma ideal de devolver objetos desde la Web API (que está unificada con MVC y usa el mismo Controller clase base) es

 public IActionResult Get() { return new OkObjectResult(new Item { Id = 123, Name = "Hero" }); } 

Darse cuenta de

  1. Vuelve con el código de estado 200 OK (es un tipo Ok de ObjectResult )
  2. Realiza la negociación de contenido, es decir, volverá en función del encabezado Accept en solicitud. Si se envía Accept: application/xml por solicitud, se devolverá como XML . Si no se envía nada, JSON es el predeterminado.

Si necesita enviar con un código de estado específico , use ObjectResult o StatusCode en StatusCode lugar. Ambos hacen lo mismo y admiten la negociación de contenido.

 return new ObjectResult(new Item { Id = 123, Name = "Hero" }) { StatusCode = 200 }; return StatusCode( 200, new Item { Id = 123, Name = "Hero" }); 

Si específicamente quieres regresar como JSON , hay dos formas

 //GET http://example.com/api/test/asjson [HttpGet("AsJson")] public JsonResult GetAsJson() { return Json(new Item { Id = 123, Name = "Hero" }); } //GET http://example.com/api/test/withproduces [HttpGet("WithProduces")] [Produces("application/json")] public Item GetWithProduces() { return new Item { Id = 123, Name = "Hero" }; } 

Darse cuenta de

  1. Ambas implementa JSON de dos maneras diferentes.
  2. Ambos ignoran la negociación de contenido.
  3. El primer método aplica JSON con el serializador específico Json(object) .
  4. El segundo método hace lo mismo al usar el atributo ResultFilter Produces() (que es un ResultFilter ) con contentType = application/json

Lea más sobre ellos en los documentos oficiales . Conozca los filtros aquí .

La clase de modelo simple que se usa en las muestras

 public class Item { public int Id { get; set; } public string Name { get; set; } } 

La forma más fácil que se me ocurrió es:

 return new JsonResult(result) { StatusCode = 201 // Status code here }; 

Esta es mi solución más fácil:

 public IActionResult InfoTag() { return Ok(new {name = "Fabio", age = 42, gender = "M"}); } 

o

 public IActionResult InfoTag() { return Json(new {name = "Fabio", age = 42, gender = "M"}); } 

Por favor refiérase al código a continuación, Usted puede administrar múltiples códigos de estado con diferentes tipos de JSON

 public async Task 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(HttpStatusCode.NotFound, "No resources found."); } return this.Request.CreateResponse>(HttpStatusCode.OK, resourceModelList, "application/json"); } } catch (Exception ex) { return this.Request.CreateResponse(HttpStatusCode.InternalServerError, "Something went wrong."); } }