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:
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.
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 typeStatusCodeResult
.
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
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 ...
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 que
AddMvc()
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.
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();
}
}
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 laAuthorsController
à 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êteAccept
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
Vous avez des méthodes prédéfinies pour les codes de statut les plus courants.
Ok(result)
renvoie _200
_ avec réponseCreatedAtRoute
renvoie _201
_ + nouvelle URL de ressourceNotFound
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"));
}
_
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
200 OK
(c'est un type Ok
de ObjectResult
)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
JSON
de deux manières différentes.Json(object)
.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; }
}
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
};
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"});
}
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 !!!"));
}
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.
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)
});