ASP.NET Core restituisce JSON con codice di stato

Sto cercando il modo corretto per restituire JSON con un codice di stato HTTP nel mio controller API Web .NET Core. Io uso per usarlo in questo modo:

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

Questo era in un’applicazione MVC 4.6 ma ora con .NET Core non ho questo IHttpActionResult Ho ActionResult e uso in questo modo:

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

Ma la risposta dal server è strana, come nell’immagine qui sotto:

inserisci la descrizione dell'immagine qui

Voglio solo che il controller API Web restituisca JSON con un codice di stato HTTP come ho fatto in Web API 2.

La versione più semplice che risponde con un JsonResult è:

 // GET: api/authors [HttpGet] public JsonResult Get() { return Json(_authorRepository.List()); } 

Tuttavia, questo non aiuterà il tuo problema perché non puoi occuparti esplicitamente del tuo codice di risposta.

Il modo per ottenere il controllo sui risultati dello stato, è necessario restituire un ActionResult che è dove è ansible sfruttare il tipo StatusCodeResult .

per esempio:

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

Nota che entrambi questi esempi precedenti provengono da un’ottima guida disponibile da Documentazione Microsoft: Formattazione dei dati di risposta


Cose extra

Il problema che ho riscontrato abbastanza spesso è che volevo un controllo più granulare sulla mia WebAPI anziché andare semplicemente con la configurazione predefinita del modello “Nuovo progetto” in VS.

Assicuriamoci di avere alcune nozioni di base …

Passaggio 1: configura il tuo servizio

Per far sì che la tua WebAPI di ASP.NET Core risponda con un object serializzato JSON con il pieno controllo del codice di stato, dovresti iniziare assicurandoti di aver incluso il servizio AddMvc() nel tuo metodo ConfigureServices che di solito si trova in Startup.cs .

È importante notare che AddMvc() includerà automaticamente il Formattatore di input / output per JSON insieme alla risposta ad altri tipi di richieste.

Se il tuo progetto richiede il pieno controllo e vuoi definire rigorosamente i tuoi servizi, come ad esempio il comportamento di WebAPI su vari tipi di richieste tra cui application/json e non rispondere ad altri tipi di richiesta (come una richiesta standard del browser), puoi definirlo manualmente con il seguente codice:

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

Noterai che ho anche incluso un modo per aggiungere i tuoi formattatori di Input / Output personalizzati, nel caso tu voglia risponderti a un altro formato di serializzazione (protobuf, parsimonia, ecc.).

Il pezzo di codice sopra è principalmente un duplicato del metodo AddMvc() . Tuttavia, stiamo implementando ciascun servizio “predefinito” da soli definendo ciascun servizio invece di andare con quello pre-spedito con il modello. Ho aggiunto il link repository nel blocco di codice, oppure puoi controllare AddMvc() dal repository GitHub. .

Nota che ci sono alcune guide che cercheranno di risolvere questo problema “annullando” le impostazioni predefinite, piuttosto che non implementarle in un primo momento … Se consideri che stiamo lavorando con Open Source, questo è un lavoro ridondante , brutto codice e francamente una vecchia abitudine che sparirà presto.


Passaggio 2: creare un controller

Vi mostrerò uno veramente semplice per ottenere la vostra domanda in ordine.

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

Passaggio 3: controlla il tipo di Content-Type e Accept

Devi assicurarti che le intestazioni Content-Type e Accept nella tua richiesta siano impostate correttamente. Nel tuo caso (JSON), vorrai configurarlo come application/json .

Se vuoi che la tua WebAPI risponda come JSON come predefinita, indipendentemente da ciò che l’intestazione della richiesta sta specificando, puoi farlo in un paio di modi .

Way 1 Come mostrato nell’articolo che ho consigliato in precedenza ( Formatting Response Data ) è ansible forzare un particolare formato a livello di Controller / Azione. Personalmente non mi piace questo approccio … ma qui è per la completezza:

Forzare un formato particolare Se si desidera limitare i formati di risposta per un’azione specifica che è ansible, è ansible applicare il filtro [Produces]. Il filtro [Produces] specifica i formati di risposta per un’azione specifica (o controller). Come la maggior parte dei filtri, questo può essere applicato all’azione, al controller o all’ambito globale.

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

Il filtro [Produces] forzerà tutte le azioni all’interno di AuthorsController per restituire le risposte formattate JSON, anche se sono stati configurati altri formattatori per l’applicazione e il client ha fornito un’intestazione Accept richiedendo un formato diverso e disponibile.

Way 2 Il mio metodo preferito è che WebAPI risponda a tutte le richieste con il formato richiesto. Tuttavia, nel caso in cui non accetti il ​​formato richiesto, ricade su un valore predefinito (es. JSON)

Innanzitutto, è necessario registrarlo nelle opzioni (è necessario rielaborare il comportamento predefinito, come indicato in precedenza)

 options.RespectBrowserAcceptHeader = true; // false by default 

Infine, semplicemente riordinando l’elenco dei formattatori che sono stati definiti nel builder dei servizi, l’host Web verrà automaticamente impostato sul formattatore che si posiziona all’inizio dell’elenco (ad esempio, posizione 0).

Ulteriori informazioni possono essere trovate in questa voce sul blog Sviluppo e strumenti Web .NET

Hai metodi predefiniti per i codici di stato più comuni.

  • Ok(result) restituisce 200 con risposta
  • CreatedAtRoute restituisce 201 + nuovo URL risorsa
  • NotFound restituisce 404
  • BadRequest restituisce 400 ecc.

Vedi BaseController.cs e Controller.cs per un elenco di tutti i metodi.

Ma se insisti davvero puoi usare StatusCode per impostare un codice personalizzato, ma non dovresti farlo perché rende il codice meno leggibile e dovrai ripetere il codice per impostare le intestazioni (come per CreatedAtRoute ).

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

Con ASP.NET Core 2.0 , il modo ideale per restituire l’object Web API (che è unificata con MVC e utilizza lo stesso Controller class base) è

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

Notare che

  1. Ritorna con 200 OK codice di stato (è un tipo Ok di ObjectResult )
  2. Effettua la negoziazione del contenuto, cioè restituirà in base all’intestazione Accept in richiesta. Se Accept: application/xml viene inviato su richiesta, verrà restituito come XML . Se non viene inviato nulla, JSON è l’impostazione predefinita.

Se è necessario inviare con uno specifico codice di stato , utilizzare invece ObjectResult o StatusCode . Entrambi fanno la stessa cosa e supportano la negoziazione del contenuto.

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

Se vuoi specificamente restituire come JSON , ci sono un paio di modi

 //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" }; } 

Notare che

  1. Entrambe impongono JSON in due modi diversi.
  2. Entrambi ignorano la negoziazione del contenuto.
  3. Il primo metodo impone JSON con serializzatore specifico Json(object) .
  4. Il secondo metodo fa lo stesso usando l’attributo Produces() (che è un ResultFilter ) con contentType = application/json

Leggi di più su di loro nei documenti ufficiali . Scopri i filtri qui .

La semplice class del modello che viene utilizzata nei campioni

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

Il modo più semplice con cui sono arrivato è:

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

Questa è la mia soluzione più semplice:

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

Si prega di fare riferimento al codice sottostante, È ansible gestire più codice di stato con diverso tipo 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."); } }