web-dev-qa-db-fra.com

ASP.NET Core renvoyer JSON avec le code d'état

Je cherche le moyen correct de renvoyer JSON avec un code de statut HTTP dans mon contrôleur API Web .NET Core. Je l'utilise comme ça:

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 cette IHttpActionResult j'ai ActionResult et je l'utilise 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 API Web renvoie JSON avec un code de statut HTTP comme je l'ai fait dans Web API 2.

111
Rossco

La version la plus basique répondant avec un JsonResult est:

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

Toutefois, cela ne va pas vous aider avec votre problème car vous ne pouvez pas gérer explicitement votre propre code de réponse.

Pour obtenir le contrôle des résultats de l’état, vous devez renvoyer un ActionResult, qui vous permet de tirer parti du type StatusCodeResult.

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 ces deux exemples ci-dessus proviennent d'un excellent guide disponible dans la documentation Microsoft: Mise en forme des données de réponse


Extra Stuff

Le problème que je rencontre souvent, c’est que je voulais un contrôle plus granulaire de mon WebAPI plutôt que de simplement utiliser la configuration par défaut du modèle "Nouveau projet" de VS.

Assurons-nous d'avoir quelques notions de base ...

Étape 1: Configurez votre service

Pour que votre WebAPI Core ASP.NET réponde avec un objet sérialisé JSON tout en contrôlant totalement le code d'état, commencez par vous assurer que vous avez inclus le service AddMvc() dans votre ConfigureServices méthode généralement trouvée dans Startup.cs.

Il est important de noter queAddMvc() inclut automatiquement le formateur d’entrée/sortie pour JSON et répond à d’autres types de demandes.

Si votre projet nécessite un contrôle total et que vous souhaitez définir strictement vos services, tels que le comportement de votre WebAPI vis-à-vis de différents types de requêtes, notamment application/json et ne répondez pas à d'autres types de demandes (telles qu'une demande de navigateur standard), vous pouvez le définir manuellement à l'aide du 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 d’ajouter vos propres formateurs d’entrée/sortie personnalisés, dans l’éventualité où vous souhaiteriez répondre à un autre format de sérialisation (protobuf, thrift, etc.).

Le morceau de code ci-dessus est principalement un duplicata de la méthode AddMvc(). Cependant, nous mettons en œuvre chacun de nos services "par défaut" en définissant chaque service au lieu d’utiliser le modèle pré-expédié avec le modèle. J'ai ajouté le lien de référentiel dans le bloc de code, ou vous pouvez extraire AddMvc()du référentiel GitHub. .

Notez que certains guides tenteront de résoudre ce problème en "annulant" les valeurs par défaut, plutôt que de ne pas tout simplement l'implémenter ... Si vous tenez compte du fait que nous travaillons maintenant avec Open Source, c’est un travail redondant, un mauvais code et franchement une vieille habitude qui va bientôt disparaître.


Étape 2: Créer un contrôleur

Je vais vous en montrer une très simple, juste pour résoudre 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 et Accept

Vous devez vous assurer que vos en-têtes Content-Type et Accept dans votre demande sont correctement définis. Dans votre cas (JSON), vous voudrez le configurer pour être application/json.

Si vous voulez que votre WebAPI réponde au format JSON par défaut, quel que soit l'en-tête de la demande, vous pouvez le faire de deux manières .

Méthode 1 Comme indiqué dans l'article que j'avais recommandé précédemment ( Formatting Response Data ), vous pouvez forcer un format particulier sur le contrôleur/Niveau d'action. Personnellement, je n'aime pas cette approche ... mais la voici:

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

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

Le filtre [Produces] force toutes les actions de la AuthorsController à renvoyer les réponses au format JSON, même si d'autres formateurs ont été configurés pour l'application et que le client a fourni un en-tête Accept demandant une réponse différente, disponible. format.

Voie 2 Ma méthode préférée consiste à ce que WebAPI réponde à toutes les demandes avec le format demandé. Cependant, dans le cas où il n'accepterait pas le format demandé, alors utiliser une valeur par défaut (c'est-à-dire JSON)

Tout d’abord, vous devrez l’inscrire 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é-ordonnant simplement la liste des formateurs définis dans le constructeur de services, l'hôte Web utilisera par défaut le formateur que vous placez en haut de la liste (position 0).

Plus d'informations peuvent être trouvées dans ceci Entrée de blog .NET Web Development and Tools

141
Svek

Vous avez des méthodes prédéfinies pour les codes de statut les plus courants.

  • Ok(result) renvoie _200_ avec réponse
  • CreatedAtRoute renvoie _201_ + nouvelle URL de ressource
  • NotFound renvoie _404_
  • BadRequest renvoie _400_ etc.

Voir BaseController.cs et Controller.cs pour 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, car cela rend le code moins lisible et vous devrez répéter le code pour définir les en-têtes (comme pour CreatedAtRoute ).

_public ActionResult IsAuthenticated()
{
    return StatusCode(200, Json("123"));
}
_
46
Tseng

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

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

Remarquerez que

  1. Il retourne avec le code de statut 200 OK (c'est un type Ok de ObjectResult)
  2. Il négocie le contenu, c’est-à-dire qu’il retournera en fonction de l’en-tête Accept de la requête. Si Accept: application/xml est envoyé dans la requête, il retournera sous la forme XML. Si rien n'est envoyé, JSON est la valeur par défaut.

S'il doit envoyer avec un code d'état spécifique , utilisez plutôt ObjectResult ou StatusCode. Les deux font la même chose et supportent la négociation de contenu.

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

Si vous voulez spécifiquement retourner en JSON , il y a deux 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" };
}

Remarquerez que

  1. Les deux appliquent JSON de deux manières différentes.
  2. Les deux ignore la négociation de 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 l'attribut Produces() (qui est un ResultFilter) avec contentType = application/json

En savoir plus sur eux dans les documents officiels . En savoir plus sur filtres ici .

La classe de modèle simple utilisée dans les exemples

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

Le moyen le plus simple que j'ai trouvé est:

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

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

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

Au lieu d'utiliser les codes d'état 404/201 en utilisant enum

     public async Task<IActionResult> Login(string email, string password)
    {
        if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
        { 
            return StatusCode((int)HttpStatusCode.BadRequest, Json("email or password is null")); 
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));

        }
        var passwordSignInResult = await _signInManager.PasswordSignInAsync(user, password, isPersistent: true, lockoutOnFailure: false);
        if (!passwordSignInResult.Succeeded)
        {
            return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));
        }
        return StatusCode((int)HttpStatusCode.OK, Json("Sucess !!!"));
    }
3
ram dev

Ce que je fais dans mes applications Ap Net Asp Net Core consiste à créer une classe qui s'étend à partir de ObjectResult et à fournir de nombreux constructeurs pour personnaliser le contenu et le code d'état. Ensuite, toutes mes actions de contrôleur utilisent l'un des costructeurs comme il convient. Vous pouvez consulter mon implémentation à l'adresse suivante: https://github.com/melardev/AspNetCoreApiPaginatedCrud

et

https://github.com/melardev/ApiAspCoreEcommerce

voici à quoi ressemble la classe (allez à mon dépôt pour le code complet):

public class StatusCodeAndDtoWrapper : ObjectResult
{



    public StatusCodeAndDtoWrapper(AppResponse dto, int statusCode = 200) : base(dto)
    {
        StatusCode = statusCode;
    }

    private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, string message) : base(dto)
    {
        StatusCode = statusCode;
        if (dto.FullMessages == null)
            dto.FullMessages = new List<string>(1);
        dto.FullMessages.Add(message);
    }

    private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, ICollection<string> messages) : base(dto)
    {
        StatusCode = statusCode;
        dto.FullMessages = messages;
    }
}

Remarquez la base (dto) que vous remplacez dto par votre objet et vous devriez être prêt à partir.

0
Melardev

Réponses géniales que j'ai trouvé ici et j'ai aussi essayé cette déclaration de retour voir StatusCode(whatever code you wish) et cela a fonctionné !!!

return Ok(new {
                    Token = new JwtSecurityTokenHandler().WriteToken(token),
                    Expiration = token.ValidTo,
                    username = user.FullName,
                    StatusCode = StatusCode(200)
                });
0
Oge Nwike