350 votes

Comment passer les données POST json à la méthode API Web en tant qu'objet ?

L'application ASP.NET MVC4 Web API définit la méthode post pour enregistrer le client. Le client est transmis au format json dans le corps de la requête POST. Le paramètre Customer de la méthode post contient des valeurs nulles pour les propriétés.

Comment résoudre ce problème pour que les données affichées soient transmises en tant qu'objet client ?

Si possible, Content-Type : application/x-www-form-urlencoded devrait être utilisé car je ne sais pas comment le changer dans la méthode javascript qui affiche le formulaire.

Contrôleur :

public class CustomersController : ApiController {

  public object Post([FromBody] Customer customer)
        {
            return Request.CreateResponse(HttpStatusCode.OK,
            new
            {
                customer = customer
            });
        }
    }
}

public class Customer
    {
        public string company_name { get; set; }
        public string contact_name { get; set; }
     }

Demande :

POST http://localhost:52216/api/customers HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8

{"contact_name":"sdfsd","company_name":"ssssd"}

561voto

Shyju Points 46555

EDITAR : 31/10/2017

Le même code/approche fonctionnera pour Asp.Net Core 2.0 également. La principale différence est que, dans asp.net core, les contrôleurs de l'api web et les contrôleurs Mvc sont fusionnés en un seul modèle de contrôleur. Donc votre type de retour peut être IActionResult ou l'une de ses mises en œuvre (Ex : OkObjectResult )


Utilisez

contentType:"application/json"

Vous devez utiliser JSON.stringify pour le convertir en chaîne JSON lorsque vous l'envoyez,

Et le model binder liera les données json à votre objet de classe.

Le code ci-dessous fonctionne bien (testé)

$(function () {
    var customer = {contact_name :"Scott",company_name:"HP"};
    $.ajax({
        type: "POST",
        data :JSON.stringify(customer),
        url: "api/Customer",
        contentType: "application/json"
    });
});

Resultado

enter image description here

contentType indique au serveur que nous envoyons les données au format JSON. Puisque nous avons envoyé une structure de données JSON, le model binding se fera correctement.

Si vous inspectez les en-têtes de la requête ajax, vous pouvez voir que l'option Content-Type est définie comme application/json .

Si vous ne spécifiez pas explicitement le type de contenu, il utilisera le type de contenu par défaut qui est application/x-www-form-urlencoded;


Modifier en novembre 2015 pour traiter d'autres problèmes éventuels soulevés dans les commentaires.

Enregistrement d'un objet complexe

Disons que vous avez une classe de modèle de vue complexe comme paramètre de la méthode d'action de l'api Web, comme ceci

public class CreateUserViewModel
{
   public int Id {set;get;}
   public string Name {set;get;}  
   public List<TagViewModel> Tags {set;get;}
}
public class TagViewModel
{
  public int Id {set;get;}
  public string Code {set;get;}
}

et le point final de votre api web est comme

public class ProductController : Controller
{
    [HttpPost]
    public CreateUserViewModel Save([FromBody] CreateUserViewModel m)
    {
        // I am just returning the posted model as it is. 
        // You may do other stuff and return different response.
        // Ex : missileService.LaunchMissile(m);
        return m;
    }
}

Au moment où nous écrivons ces lignes, ASP.NET MVC 6 est la dernière version stable et dans MVC6, les contrôleurs de l'API Web et les contrôleurs MVC héritent tous deux de l'élément suivant Microsoft.AspNet.Mvc.Controller classe de base.

Pour envoyer des données à la méthode du côté client, le code suivant devrait fonctionner correctement

//Build an object which matches the structure of our view model class
var model = {
    Name: "Shyju",
    Id: 123,
    Tags: [{ Id: 12, Code: "C" }, { Id: 33, Code: "Swift" }]
};

$.ajax({
    type: "POST",
    data: JSON.stringify(model),
    url: "../product/save",
    contentType: "application/json"
}).done(function(res) {       
    console.log('res', res);
    // Do something with the result :)
});

La liaison de modèle fonctionne pour certaines propriétés, mais pas toutes ! Pourquoi ?

Si vous ne décorez pas le paramètre de la méthode de l'api web avec l'option [FromBody] attribut

[HttpPost]
public CreateUserViewModel Save(CreateUserViewModel m)
{
    return m;
}

Et envoyez le modèle (objet javascript brut, pas au format JSON) sans spécifier la valeur de la propriété contentType.

$.ajax({
    type: "POST",
    data: model,
    url: "../product/save"
}).done(function (res) {
     console.log('res', res);
});

La liaison de modèle fonctionnera pour les propriétés plates sur le modèle, pas pour les propriétés où le type est complexe/un autre type. Dans notre cas, Id y Name seront correctement liées au paramètre m Mais le Tags sera une liste vide.

Le même problème se posera si vous utilisez la version courte, $.post qui utilisera le Content-Type par défaut lors de l'envoi de la demande.

$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});

4 votes

Je ne suis pas sûr de ce que j'ai fait, mais je suis revenu ce matin et je suis de nouveau dans le même bateau. L'objet est nul dans le contrôleur. C'est reparti.

0 votes

Ok, ça fait deux heures et je n'arrive pas à comprendre. Dans mon cas, juste pour tester, je poste depuis fiddler. Le type de contenu est défini sur Content-Type : application/json. J'ai un viewmodel avec lequel j'essaie de lier les données. Je suis allé jusqu'à modifier le modèle d'affichage pour qu'il ne contienne qu'un seul élément, { \"AssetName\":\"testname\" } et mon contrôleur ressemble à ceci [HttpPost] public HttpResponseMessage LogValidation([FromBody]myViewModel myviewmodel){ le message atteint le contrôleur mais myviewmodel est toujours nul.

0 votes

@Grayson pouvez-vous poster un exemple de solution minimisée pour que je puisse y jeter un œil ?

71voto

Vaibhav Points 517

Travailler avec POST dans webapi peut être délicat ! Je voudrais ajouter à la réponse déjà correcte

Nous nous concentrerons spécifiquement sur le POST car le traitement du GET est trivial. Je ne pense pas que beaucoup de gens chercheraient à résoudre un problème avec GET avec webapis. Quoi qu'il en soit

Si votre question est - Dans MVC Web Api, comment - Utiliser des noms de méthodes d'action personnalisés autres que les verbes HTTP génériques ? - Effectuer des enregistrements multiples ? - Afficher plusieurs types simples ? - Afficher des types complexes via jQuery ?

Dans ce cas, les solutions suivantes peuvent vous aider :

Premièrement, pour utiliser Personnalisé Méthodes d'action dans l'API Web, ajouter une route d'API Web comme :

public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute(
        name: "ActionApi",
        routeTemplate: "api/{controller}/{action}");
}

Et puis vous pouvez créer des méthodes d'action comme :

[HttpPost]
public string TestMethod([FromBody]string value)
{
    return "Hello from http post web api controller: " + value;
}

Maintenant, lancez le jQuery suivant depuis la console de votre navigateur

$.ajax({
    type: 'POST',
    url: 'http://localhost:33649/api/TestApi/TestMethod',
    data: {'':'hello'},
    contentType: 'application/x-www-form-urlencoded',
    dataType: 'json',
    success: function(data){ console.log(data) }
});

Deuxièmement, pour exécuter plusieurs postes C'est simple, créez plusieurs méthodes d'action et décorez-les avec l'attribut [HttpPost]. Utilisez l'attribut [ActionName("MyAction")] pour attribuer des noms personnalisés, etc. Nous reviendrons à jQuery dans le quatrième point ci-dessous.

Troisièmement, tout d'abord, poster plusieurs SIMPLE en une seule action n'est pas possible. De plus, il existe un format spécial pour afficher ne serait-ce qu'un un seul type simple (à part le passage du paramètre dans la chaîne de requête ou le style REST). C'est à ce moment-là que je me suis tapé la tête avec des clients REST (comme Fiddler et l'extension client Advanced REST de Chrome) et que j'ai cherché sur le web pendant près de 5 heures, quand finalement, l'URL suivante s'est avérée utile. Je citerai le contenu pertinent car le lien pourrait être mort !

Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"na@Turbo.Tina"}

PS : J'ai remarqué que le syntaxe particulière ?

http://forums.asp.net/t/1883467.aspx?The+valeur+reçue+est+nulle+lorsque+je+essaie+de+poster+vers+mon+Api+Web

Bref, oublions cette histoire. Continuons :

Quatrièmement, comptabilisation des types complexes via jQuery, bien sûr, $.ajax() va rapidement entrer en jeu :

Disons que la méthode d'action accepte un objet Personne qui a un identifiant et un nom. Donc, à partir de javascript :

var person = { PersonId:1, Name:"James" }
$.ajax({
    type: 'POST',
    url: 'http://mydomain/api/TestApi/TestMethod',
    data: JSON.stringify(person),
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function(data){ console.log(data) }
});

Et l'action ressemblera à ça :

[HttpPost]
public string TestMethod(Person person)
{
    return "Hello from http post web api controller: " + person.Name;
}

Tout ce qui précède, a fonctionné pour moi !! A la vôtre !

4 votes

Il semble que je rencontre ce problème tous les quelques mois. La plupart du temps, je finis par le résoudre, mais cette fois, j'ai abandonné. Aucun des conseils ci-dessus ne me permet de résoudre le problème, alors j'ai décidé d'abandonner cette approche. Si c'est si difficile à réaliser, à quoi bon ? Ce n'est qu'une commodité de toute façon - il suffit de prendre le contenu sous forme de chaîne et d'utiliser newtonsoft pour le transformer. C'est fait. Il m'a fallu probablement 30 secondes pour le résoudre de la manière "difficile" après avoir essayé pendant environ une heure de le résoudre de la manière "facile". Je n'aime pas trop cette approche, mais y a-t-il un problème fondamental ?

0 votes

PS : Dans WebApi2, nous pouvons maintenant utiliser les Route Decorators. Ce problème est donc principalement résolu. asp.net/web-api/overview/web-api-routing-and-actions/

2 votes

J'aimerais ajouter une observation. Parfois, la raison pour laquelle le model-binding échoue (null) du côté de la WebAPI, lorsqu'on passe un type complexe (ex : DTO), est qu'une ou plusieurs propriétés du modèle sont incompatibles (ou ne sont pas analysées). Par exemple, une propriété Guid se voit attribuer un GUID invalide. Dans ce cas, essayez d'utiliser les valeurs par défaut/vides pour toutes les propriétés de l'objet et réessayez.

12voto

Andy Points 1145

Je viens de jouer avec et j'ai découvert un résultat plutôt étrange. Disons que vous avez des propriétés publiques sur votre classe en C# comme ceci :

public class Customer
{
    public string contact_name;
    public string company_name;
}

alors vous devez faire l'astuce JSON.stringify comme suggéré par Shyju et l'appeler comme ceci :

var customer = {contact_name :"Scott",company_name:"HP"};
$.ajax({
    type: "POST",
    data :JSON.stringify(customer),
    url: "api/Customer",
    contentType: "application/json"
});

Cependant, si vous définissez des getters et setters sur votre classe comme ceci :

public class Customer
{
    public string contact_name { get; set; }
    public string company_name { get; set; }
}

alors vous pouvez l'appeler beaucoup plus simplement :

$.ajax({
    type: "POST",
    data :customer,
    url: "api/Customer"
});

Cela utilise l'en-tête HTTP :

Content-Type:application/x-www-form-urlencoded

Je ne suis pas tout à fait sûr de ce qui se passe ici mais cela ressemble à un bug (fonctionnalité ?) dans le framework. On peut supposer que les différentes méthodes de liaison font appel à différents "adaptateurs", et que si l'adaptateur pour application/json fonctionne avec les propriétés publiques, celui pour les données codées par formulaire ne le fait pas.

Je n'ai aucune idée de ce qui serait considéré comme la meilleure pratique.

8 votes

Les propriétés et les champs sont différents. Les propriétés sont la meilleure pratique. Ce que vous appelez propriétés dans ce premier exemple sont en fait des champs. Lorsque vous mettez un get/set sur eux, ils ont alors un champ de soutien créé automatiquement, ce qui en fait des propriétés.

0 votes

C'est tellement vrai, et étrange. Les classes normales ne comportant que des champs ne se lient pas aux messages du formulaire, mais les propriétés le font. BTW : Cela n'explique toujours pas pourquoi c'est le cas... ? Je ne peux que supposer que la logique interne ne lie que les données JSON aux champs, et les données de formulaire aux propriétés, et c'est tout... ?

1 votes

C'est le cas parce que le code ne recherche que les propriétés. Puisque l'utilisation de champs publics est no meilleure pratique, l'équipe MS a décidé de ne pas autoriser les no les scénarios de meilleures pratiques, une bonne raison, selon moi.

1voto

Dilip Nannaware Points 863

Utilisez le JSON.stringify() pour obtenir la chaîne au format JSON, assurez-vous que lors de l'appel AJAX, vous passez les attributs mentionnés ci-dessous :

  • contentType : application/json

Voici le code jquery pour faire un appel ajax à asp.net web api :

var product =
    JSON.stringify({
        productGroup: "Fablet",
        productId: 1,
        productName: "Lumia 1525 64 GB",
        sellingPrice: 700
    });

$.ajax({
    URL: 'http://localhost/api/Products',
    type: 'POST',
    contentType: 'application/json',
    data: product,
    success: function (data, status, xhr) {
        alert('Success!');
    },
    error: function (xhr, status, error) {
        alert('Update Error occurred - ' + error);
    }
});

2 votes

DataType n'est pas nécessaire.

0voto

scottyj Points 1

Assurez-vous que votre service WebAPI attend un objet fortement typé dont la structure correspond au JSON que vous transmettez. Et assurez-vous que vous stringifiez le JSON que vous envoyez.

Voici mon JavaScript (utilisant AngluarJS) :

$scope.updateUserActivity = function (_objuserActivity) {
        $http
        ({
            method: 'post',
            url: 'your url here',
            headers: { 'Content-Type': 'application/json'},
            data: JSON.stringify(_objuserActivity)
        })
        .then(function (response)
        {
            alert("success");
        })
        .catch(function (response)
        {
            alert("failure");
        })
        .finally(function ()
        {
        });

Et voici mon contrôleur WebAPI :

[HttpPost]
[AcceptVerbs("POST")]
public string POSTMe([FromBody]Models.UserActivity _activity)
{
    return "hello";
}

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