223 votes

ASP.NET Core renvoie JSON avec un code d'état

Je cherche la manière correcte de renvoyer JSON avec un code d'état HTTP dans mon contrôleur API Web .NET Core. Je l'utilisais comme ceci :

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

C'était dans une application MVC 4.6, mais maintenant avec .NET Core, je ne semble pas avoir ce problème. IHttpActionResult J'ai ActionResult et l'utiliser comme ceci :

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

Mais la réponse du serveur est bizarre, comme dans l'image ci-dessous :

enter image description here

Je veux juste que le contrôleur de l'API Web renvoie JSON avec un code d'état HTTP comme dans l'API Web 2.

1 votes

Les méthodes "ok" renvoient 200 comme code d'état. Les méthodes prédéfinies couvrent tous les cas courants. Pour retourner 201 (+en-tête avec le nouvel emplacement de la ressource), vous utilisez CreatedAtRoute méthode, etc.

258voto

Svek Points 6227

La version la plus basique répond avec un JsonResult es:

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

Toutefois, cela ne vous aidera pas à résoudre votre problème, car vous ne pouvez pas gérer explicitement votre propre code de réponse.

Pour contrôler les résultats de l'état, il faut renvoyer une valeur de type ActionResult et c'est là que vous pouvez alors profiter de la StatusCodeResult type.

par exemple :

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

Notez que les deux exemples ci-dessus proviennent d'un excellent guide disponible dans la documentation de Microsoft : Formatage des données de réponse


Extra Stuff

Le problème que je rencontre assez souvent est que je voulais un contrôle plus granulaire de ma WebAPI plutôt que de me contenter de la configuration par défaut du modèle "Nouveau projet" dans VS.

Assurons-nous que vous avez bien compris les principes de base...

Étape 1 : Configurez votre service

Pour que votre WebAPI ASP.NET Core réponde avec un objet sérialisé JSON tout en ayant le contrôle total du code d'état, vous devez commencer par vous assurer que vous avez inclus l'objet AddMvc() dans votre ConfigureServices que l'on trouve habituellement dans Startup.cs .

Il est important de noter que AddMvc() inclura automatiquement le formateur d'entrée/sortie pour JSON tout en répondant aux autres types de demandes.

Si votre projet nécessite contrôle total et que vous souhaitez définir strictement vos services, par exemple la manière dont votre WebAPI se comportera face à différents types de demandes, notamment application/json et ne pas répondre à d'autres types de requête (comme une requête standard de navigateur), vous pouvez le définir manuellement avec le code suivant :

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

Vous remarquerez que j'ai également inclus un moyen pour vous d'ajouter vos propres formateurs d'entrée/sortie personnalisés, dans le cas où vous voudriez répondre à un autre format de sérialisation (protobuf, thrift, etc.).

Le morceau de code ci-dessus est en grande partie une duplication de la fonction AddMvc() méthode. Cependant, nous implémentons chaque service "par défaut" par nous-mêmes en définissant chaque service au lieu d'utiliser celui qui est déjà fourni avec le modèle. J'ai ajouté le lien du référentiel dans le bloc de code, ou vous pouvez vérifier AddMvc() à partir du dépôt GitHub. .

Notez qu'il existe des guides qui tentent de résoudre ce problème en "annulant" les valeurs par défaut, plutôt que de ne pas les implémenter en premier lieu... Si vous tenez compte du fait que nous travaillons maintenant avec l'Open Source, c'est du travail redondant, du mauvais code et franchement une vieille habitude qui disparaîtra bientôt.


Étape 2 : Créer un contrôleur

Je vais vous en montrer un très simple, pour répondre à votre question.

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

Étape 3 : Vérifiez votre Content-Type y Accept

Vous devez vous assurer que votre Content-Type y Accept dans votre demande sont correctement configurés. Dans votre cas (JSON), vous voudrez le configurer pour qu'il soit application/json .

Si vous souhaitez que votre WebAPI réponde en JSON par défaut, indépendamment de ce que l'en-tête de la requête spécifie, vous pouvez le faire dans un fichier de type deux façons .

Voie 1 Comme le montre l'article que j'ai recommandé précédemment ( Formatage des données de réponse ), vous pourriez forcer un format particulier au niveau du contrôleur/de l'action. Personnellement, je n'aime pas cette approche... mais la voici pour être complet :

Forcer un format particulier Si vous souhaitez restreindre les formats de réponse pour une action spécifique, vous pouvez appliquer le filtre filtre [Produces]. Le filtre [Produces] spécifie les formats de réponse de réponse pour une action (ou un contrôleur) spécifique. Comme la plupart des filtres, ce filtre peut être appliqué à l'action, au contrôleur ou à la portée globale.

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

El [Produces] forcera toutes les actions dans le cadre de l AuthorsController pour renvoyer des réponses au format JSON, même si d'autres formatters étaient configurés pour l'application et que le client fournissait une adresse Accept demandant un format différent et disponible.

Voie 2 Ma méthode préférée est que l'interface WebAPI réponde à toutes les demandes avec le format demandé. Toutefois, au cas où elle n'accepterait pas le format demandé, il faudrait que l'API Web réponde à toutes les demandes avec le format demandé. solution de repli à une valeur par défaut (c'est-à-dire JSON).

Tout d'abord, vous devrez l'enregistrer dans vos options (nous devons retravailler le comportement par défaut, comme indiqué précédemment).

options.RespectBrowserAcceptHeader = true; // false by default

Enfin, en réorganisant simplement la liste des formateurs qui ont été définis dans le constructeur de services, l'hôte Web choisira par défaut le formateur que vous placez en haut de la liste (c'est-à-dire en position 0).

Vous trouverez de plus amples informations dans ce document Entrée du blog sur le développement et les outils Web .NET

2 votes

Pour prolonger ce sujet, j'ai créé un guide supplémentaire et plus complet sur la mise en œuvre de la WebAPI ici : stackoverflow.com/q/42365275/3645638

0 votes

Lors du paramétrage : RespectBrowserAcceptHeader = true ; Vous n'expliquez pas pourquoi vous le faites, et il est généralement inutile et erroné de le faire. Les navigateurs demandent du html, et ne devraient donc pas affecter la sélection du formateur de quelque manière que ce soit (ce que chrome fait malheureusement en demandant du XML). En bref, c'est quelque chose que je ne ferais pas, et la solution de repli que vous spécifiez est déjà le comportement par défaut.

0 votes

@YishaiGalatzer Le thème principal de cette partie de ma réponse était de souligner comment décharger l'intergiciel par défaut entre le client et la logique de l'API. A mon avis, RespectBrowserAcceptHeader est critique lorsque vous mettez en œuvre l'utilisation d'un sérialiseur alternatif ou, plus communément, lorsque vous voulez vous assurer que vos clients n'envoient pas de requêtes mal formées. C'est pourquoi j'ai mis l'accent sur "Si votre projet nécessite contrôle total et vous voulez définir strictement votre service" et notez le bloc de citation en surbrillance au-dessus de cette déclaration également.

74voto

Tseng Points 2687

Vous disposez de méthodes prédéfinies pour les codes d'état les plus courants.

  • Ok(result) renvoie à 200 avec réponse
  • CreatedAtRoute renvoie à 201 + nouvelle URL de la ressource
  • NotFound renvoie à 404
  • BadRequest renvoie à 400 etc.

Ver BaseController.cs y Controller.cs pour obtenir la liste de toutes les méthodes.

Mais si vous insistez vraiment, vous pouvez utiliser StatusCode pour définir un code personnalisé, mais vous ne devriez vraiment pas le faire car cela rend le code moins lisible et vous devrez répéter le code pour définir les en-têtes (comme dans le cas de CreatedAtRoute ).

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

1 votes

Cela m'a donné un aperçu de ma réponse ci-dessous. Je vous remercie.

0 votes

Ce code n'est pas correct pour ASP.NET Core 2.2. Je viens de l'essayer et il se sérialise en JSON le site ActionResult créé par le Json() méthode. Elle n'inclut pas directement la chaîne "123".

1 votes

@amedina : C'est ma faute, il suffit d'enlever le Json(...) et passer la chaîne à StatusCode

68voto

Arghya C Points 1381

Avec ASP.NET Core 2.0 le moyen idéal de renvoyer un objet de Web API (qui est unifié avec MVC et utilise la même classe de base Controller ) est

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

Remarquez que

  1. Il revient avec 200 OK (c'est un code d'état Ok type de ObjectResult )
  2. Il fait de la négociation de contenu, c'est-à-dire qu'il renvoie sur la base de Accept dans la demande. Si Accept: application/xml est envoyé dans la requête, il sera retourné en tant que XML . Si rien n'est envoyé, JSON est par défaut.

S'il doit envoyer avec un code d'état spécifique utiliser ObjectResult o StatusCode à la place. Les deux font la même chose, et prennent en charge la négociation du contenu.

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

ou encore plus finement avec ObjectResult :

 Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection myContentTypes = new Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection { System.Net.Mime.MediaTypeNames.Application.Json };
 String hardCodedJson = "{\"Id\":\"123\",\"DateOfRegistration\":\"2012-10-21T00:00:00+05:30\",\"Status\":0}";
 return new ObjectResult(hardCodedJson) { StatusCode = 200, ContentTypes = myContentTypes };

Si vous voulez spécifiquement retour en JSON il y a plusieurs façons

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

Remarquez que

  1. Les deux appliquent JSON de deux manières différentes.
  2. Les deux ignorent la négociation du contenu.
  3. La première méthode applique JSON avec un sérialiseur spécifique. Json(object) .
  4. La deuxième méthode fait de même en utilisant Produces() (qui est un ResultFilter ) avec contentType = application/json

Pour en savoir plus, consultez le site les documents officiels . En savoir plus filtres ici .

La classe modèle simple qui est utilisée dans les échantillons

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

10 votes

Il s'agit d'une bonne réponse car elle se concentre sur la question et explique brièvement certains aspects pratiques.

42voto

Gerald Hughes Points 1807

La méthode la plus simple que j'ai trouvée est la suivante :

var result = new Item { Id = 123, Name = "Hero" };

return new JsonResult(result)
{
    StatusCode = StatusCodes.Status201Created // Status code here 
};

3 votes

Je pense que c'est mieux que la réponse de @tseng, car sa solution comprend des champs dupliqués pour les codes d'état, etc.

2 votes

Une amélioration que vous pouvez apporter est d'utiliser les StatusCodes définis dans Microsoft.AspNetCore.Http comme ceci : return new JsonResult(new { }) { StatusCode = StatusCodes.Status404NotFound } ;

3 votes

Cela devrait être la réponse acceptée. Bien qu'il existe des moyens de configurer universellement le json, nous devons parfois travailler avec des points de terminaison anciens et les paramètres peuvent être différents. Jusqu'à ce que nous puissions cesser de prendre en charge certains points de terminaison hérités, c'est le meilleur moyen d'avoir un contrôle total.

20voto

Fabio Points 51

C'est ma solution la plus simple :

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

ou

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

0 votes

Merci, vous avez sauvé ma journée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X