web-api corps POST objet toujours null
Je suis encore à apprendre web API, donc pardonnez-moi si ma question semble stupide.
J'ai cela dans mon StudentController
:
public HttpResponseMessage PostStudent([FromBody]Models.Student student)
{
if (DBManager.createStudent(student) != null)
return Request.CreateResponse(HttpStatusCode.Created, student);
else
return Request.CreateResponse(HttpStatusCode.BadRequest, student);
}
Afin de tester si cela fonctionne, je suis en utilisant Google Chrome est une extension de "Facteur" pour construire la requête HTTP POST pour le tester.
C'est mon cru requête POST:
POST /api/Student HTTP/1.1
Host: localhost:1118
Content-Type: application/json
Cache-Control: no-cache
{"student": [{"name":"John Doe", "age":18, "country":"United States of America"}]}
"student"
est censé être un objet, mais quand je debug de l'application, l'API reçoit l'étudiant objet, mais le contenu est toujours NULL
.
- Juste une note pour toute personne qui trouve cela plus tard (comme je viens de le faire) tout en regardant un problème similaire: Web API de retour JSON contenant de l'exception (comme si tu l'avais pris l'exception dans votre code) qui peut être utilisé pour diagnostiquer le problème. Semble évident, mais je pense que je ne suis pas la seule personne qui n'a pas pensé à vérifier la réponse et supposé que c'était juste un standard code HTTP de la réponse!
Vous devez vous connecter pour publier un commentaire.
FromBody est un étrange attribut que l'entrée POST valeurs doivent être dans un format spécifique pour le paramètre d'être non-nulle, quand il n'est pas un type primitif. (étudiant ici)
{"name":"John Doe", "age":18, "country":"United States of America"}
que le json.[FromBody]
attribut et essayez la solution. Il doit travailler pour non-types primitifs. (étudiant)[FromBody]
attribut, l'autre option est d'envoyer les valeurs dans=Value
format, plutôt que dekey=value
format. Cela signifie que votre valeur de la clé destudent
doit être une chaîne vide...Il y a aussi d'autres options pour écrire un modèle de liaison personnalisé pour les élèves de la classe et de l'attribut du paramètre personnalisé de votre classeur.
Je cherchais une solution à mon problème, pour quelques minutes, alors je vais partager ma solution.
Votre modèle a besoin d'avoir un vide/constructeur par défaut, sinon le modèle ne peut pas être créé, évidemment.
Être prudent lors de refactoring. 😉
Je passe plusieurs heures avec ce problème... 🙁 Getters et les setters sont NÉCESSAIRES dans les paramètres POST de déclaration d'objet. Je ne recommande pas d'utiliser de simples objets de données (string,int, ...) car ils nécessitent des le format de la requête.
Ne fonctionne pas lorsque:
Fonctionne bien lorsque:
internal
et qui a été à l'origine du problème.C'est un peu ancienne et ma réponse ira jusqu'à la dernière place, mais même alors, j'aimerais partager mon expérience.
Essayé toutes les suggestions, mais toujours avec la même valeur "null dans une [FromBody].
Enfin trouvé, il était tout au sujet de la Date format de sérialisation JSON la date de fin de la propriété de mon Angulaire de l'Objet.
Aucune erreur n'a été levée, vient de recevoir un vide FromBody de l'objet....
Si l'une des valeurs de la demande de l'objet JSON ne sont pas du même type que prévu par le service de la
[FromBody]
argument seranull
.Par exemple, si le âge propriété dans le json avait un
float
valeur:mais l'API service s'attend à ce qu'il soit un
int
puis
student
seranull
. (Pas de messages d'erreur sera envoyé dans la réponse, sauf si aucune référence null vérifier).Si l'utilisation de Facteur, assurez-vous que:
J'étais bêtement en essayant d'envoyer mon JSON comme des données de formulaire, duh...
TL;DR: Ne pas utiliser [FromBody], mais rouler votre propre version de celui-ci avec une meilleure gestion des erreurs. Raisons données ci-dessous.
D'autres réponses décrire de nombreuses causes possibles de ce problème. Cependant, la cause est que
[FromBody]
a tout simplement terrible erreur de manipulation, ce qui le rend presque inutile dans le code de production.Par exemple, l'une des plus typiques des raisons pour le paramètre à
null
est que le corps de la requête a syntaxe non valide (par exemple, invalid JSON). Dans ce cas, une logique de l'API serait de retour400 BAD REQUEST
et les plus raisonnables framework web serait de faire cela automatiquement. Cependant, ASP.NET l'API Web n'est pas raisonnable à cet égard. Il suffit simplement de définir le paramètre ànull
, et à la demande du gestionnaire doit alors "manuel" de code pour vérifier si le paramètre estnull
.Beaucoup de réponses données ici sont donc incomplets en ce qui concerne la gestion des erreurs, et un buggy ou un client malveillant peut entraîner un comportement inattendu du côté du serveur par l'envoi d'une requête invalide, qui (dans le meilleur des cas) de jeter un
NullReferenceException
quelque part et revenir à un état incorrect de500 INTERNAL SERVER ERROR
ou, pire encore, de faire quelque chose d'inattendu ou d'accident ou d'exposer une faille de sécurité.Une bonne solution serait d'écrire une coutume "
[FromBody]
" attribut qui fait la bonne gestion d'erreur et renvoie bon codes d'état, idéalement avec des informations de diagnostic à l'aide du client de développeurs.Une solution qui pourrait aider (pas encore testé) est de rendre les paramètres requis, comme suit: https://stackoverflow.com/a/19322688/2279059
Suivantes maladroit solution fonctionne aussi:
Ce n' (espérons-le) de la bonne gestion des erreurs, mais il est moins déclarative. Si, par exemple, vous utilisez Swagger pour le document de votre API, il ne saura pas le type de paramètre, ce qui signifie que vous besoin de trouver une solution de contournement manuelle pour documenter vos paramètres. C'est juste pour illustrer ce que
[FromBody]
doit être en train de faire.EDIT: A moins maladroit solution est de vérifier
ModelState
: https://stackoverflow.com/a/38515689/2279059EDIT: Il semble que
ModelState.IsValid
n'est pas, comme on pourrait s'y attendre, mis àfalse
si vous utilisezJsonProperty
avecRequired = Required.Always
et un paramètre est manquant. C'est donc aussi inutile.Cependant, à mon avis, toute solution qui nécessite l'écriture de code supplémentaire dans chaque gestionnaire de requêtes est inacceptable. Dans une langue comme .NET, avec de puissants sérialisation des capacités, et dans un cadre comme ASP.NET l'API Web, la demande de validation doit être automatique et intégrée, et il est totalement faisable, même si Microsoft ne fournit pas suffisamment intégré dans les outils.
J'ai essayé d'utiliser le [FromBody], cependant, j'ai essayé de remplir une variable de chaîne, car l'entrée sera de changer et j'ai juste besoin de le transmettre à un service principal, mais c'était toujours la valeur null
J'ai donc modifié la signature de la méthode à utiliser une classe dynamique et ensuite de les convertir à la chaîne
Cela fonctionne bien.
Il peut être utile d'ajouter le suivi dans le json sérialiseur de sorte que vous pouvez voir ce qui est quand les choses vont mal.
Définir un ITraceWriter mise en œuvre de montrer leur sortie de débogage comme:
Puis dans votre WebApiConfig faire:
(peut-être l'envelopper dans un #if DEBUG)
J'ai eu le même problème.
Dans mon cas, le problème a été en
public int? CreditLimitBasedOn { get; set; }
bien que j'ai eu.mon JSON avait la valeur
"CreditLimitBasedOn":true
quand Il doit contenir un entier. Cette propriété a empêché l'ensemble de l'objet désérialisé sur ma méthode de l'api.Peut-être quelqu'un il vous sera utile: vérifiez les modificateurs d'accès pour votre DTO/Modèle de la classe de propriétés, ils devraient être public. Dans mon cas, au cours de refactoring de domaine objet internes ont été déplacés à DTO comme ceci:
DTO est finement passé pour le client, mais quand vient le temps de passer de l'objet sur le serveur, elle n'avait que des champs vides (null/valeur par défaut). Retrait "interne" remet les choses dans l'ordre, permettant de désérialisation mechanizm à écrire propriétés de l'objet.
Dans mon cas, le problème était le
DateTime
objet j'ai été l'envoi. J'ai créé unDateTime
avec "aaaa-MM-jj", et leDateTime
qui a été requise par l'objet, j'ai été la cartographie de besoin "HH-mm-ss" aswell. Donc, en ajoutant "00-00" a résolu le problème (l'objet est nulle à cause de cela).Vérifier si
JsonProperty
attribut est défini sur les champs qui viennent comme nulle - c'est peut-être qu'ils sont mappées à différentes json bien-noms.[DataMember]
attribut pour obtenir mes valeurs des propriétés pour se propager.J'ai atteint ce problème de nombreuses fois, mais en fait, c'est assez facile à déterminer la cause.
Voici aujourd'hui par exemple. Je appeler mon POST de service avec un
AccountRequest
objet, mais quand je mets un point d'arrêt au début de cette fonction, la valeur du paramètre a toujours éténull
. Mais pourquoi ?!À identifier le problème, modifiez le paramètre type de
string
, ajoutez une ligne pour obtenirJSON.Net
pour désérialiser l'objet dans le type que vous attendiez, et de mettre un point d'arrêt sur cette ligne:Maintenant, quand vous essayez cela, si le paramètre est toujours vide ou
null
, alors vous ne sont tout simplement pas appeler le service correctement.Toutefois, si la chaîne ne contenir une valeur, alors la
DeserializeObject
devrait vous orienter vers la cause du problème, et ne parviennent pas à convertir votre chaîne de caractères dans le format désiré. Mais avec les premières (string
) données qu'il essaie de désérialiser, vous devriez maintenant être en mesure de voir ce qui est erroné avec votre valeur de paramètre.(Dans mon cas, nous avons été en appelant le service avec un
AccountRequest
objet qui avait été accidentellement sérialisé deux fois !)J'ai utilisé HttpRequestMessage et mon problème a résolu après l'avoir fait beaucoup de recherches
Juste pour ajouter mon histoire à ce fil.
Mon modèle:
Au-dessus de l'objet ne pouvait pas être sérialisé dans mon API Contrôleur et pourrait toujours retourner la valeur null. La question a été avec l'Id de type Guid: chaque fois que j'ai passé chaîne vide comme un Identifiant (naïf qu'il sera automatiquement converti à
Guid.Empty
) à partir de mon interface, j'ai reçu l'objet null comme[FromBody]
paramether.Solution était soit de
Guid
valeurGuid
àString
Dirait qu'il y a beaucoup de différentes causes de ce problème...
J'ai trouvé que l'ajout d'un
OnDeserialized
de rappel pour le modèle de classe a causé le paramètre d'être toujoursnull
. Raison exacte inconnue.J'ai eu ce problème dans mon .NET Framework Web API, parce que mon modèle a existé dans un .NET Standard projet qui fait référence à une version différente des annotations de données.
L'ajout de la ReadAsAsync ligne ci-dessous mis en évidence la cause pour moi:
Si c'est parce que de API Web 2 a couru dans un désérialisation problème dû au décalage types de données, il est possible de trouver d'où il a échoué en inspectant le flux de contenu. Il va lire jusqu'à ce qu'il rencontre une erreur, donc si vous lisez le contenu comme une chaîne de caractères, vous devriez avoir le retour de la moitié des données que vous avez posté:
Fixer ce paramètre, et il devrait en faire plus la prochaine fois (ou réussir si vous avez de la chance!)...
Après Trois jours de recherches, et aucune des solutions ci-dessus a travaillé pour moi , j'ai trouvé une autre approche de ce problème dans ce Lien:
HttpRequestMessage
J'ai utilisé l'une des solutions à ce site