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

304

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

Comment résoudre ce problème afin que les données publié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 publie le formulaire.

Manette:

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"}
Andrus
la source

Réponses:

525

EDIT : 31/10/2017

Le même code / approche fonctionnera également pour Asp.Net Core 2.0 . La principale différence est que, dans le noyau asp.net, les contrôleurs Web API et les contrôleurs Mvc sont fusionnés en un seul modèle de contrôleur. Ainsi , votre type de retour est peut - être IActionResultou l' un de ses mise en œuvre (Ex: OkObjectResult)


Utilisation

contentType:"application/json"

Vous devez utiliser la JSON.stringifyméthode pour la convertir en chaîne JSON lorsque vous l'envoyez,

Et le classeur de modèle liera les données json à votre objet de classe.

Le code ci-dessous fonctionnera bien (testé)

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

Résultat

entrez la description de l'image ici

contentTypeLa propriété indique au serveur que nous envoyons les données au format JSON. Puisque nous avons envoyé une structure de données JSON, la liaison du modèle se fera correctement.

Si vous inspectez les en-têtes de la demande ajax, vous pouvez voir que la Content-Typevaleur est définie sur application/json.

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


Modifier le novembre 2015 pour résoudre d'autres problèmes possibles soulevés dans les commentaires

Publier un objet complexe

Supposons que vous ayez une classe de modèle de vue complexe comme paramètre de méthode d'action de l'API Web comme celui-ci

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 votre point de terminaison de l'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 d'écrire ces lignes, ASP.NET MVC 6 est la dernière version stable et dans MVC6, les contrôleurs Web api et les contrôleurs MVC héritent de la Microsoft.AspNet.Mvc.Controllerclasse de base.

Pour envoyer des données à la méthode du côté client, le code ci-dessous 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 pour toutes! Pourquoi ?

Si vous ne décorez pas le paramètre de la méthode API Web avec l' [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 du modèle, pas pour les propriétés où le type est complexe / un autre type. Dans notre cas, Idet les Namepropriétés seront correctement liées au paramètre m, mais la Tagspropriété sera une liste vide.

Le même problème se produira si vous utilisez la version courte, $.postqui 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);
});
Shyju
la source
4
Je ne sais pas ce que j'ai fait, mais je suis revenu ce matin et je suis retourné dans le même bateau. L'objet est nul dans le contrôleur. c'est reparti lol
Grayson
1
assurez-vous que le type de contenu est écrit "Content-Type: application / json" lorsque vous utilisez fiddler. À votre santé!
ioWint
1
Vous m'avez simplement résolu une journée de travail !!! Cette petite fonction "JSON.stringify (data)" l'a fait!
Gil Allen
1
Gardez à l'esprit que si vous faites cela (changez l'en-tête Content-Type) et que vous faites une requête CORS, jQuery commencera à ajouter des requêtes OPTIONS de contrôle en amont avant votre POST que le serveur devra gérer.
Arbiter
1
En raison du problème avec les types complexes, je pense que c'est une habitude de simplement spécifier 'contentType:' application / json; ' et json stringify l'objet js, puis il n'est pas nécessaire d'utiliser l'attribut [FromBody].
BornToCode
69

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

Se concentrera spécifiquement sur POST, car traiter avec GET est trivial. Je ne pense pas que beaucoup 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éthode d'action personnalisés autres que les verbes HTTP génériques? - Effectuer plusieurs publications? - Poster plusieurs types simples? - Poster des types complexes via jQuery?

Ensuite, les solutions suivantes peuvent vous aider:

Tout d'abord, pour utiliser des méthodes d'action personnalisées dans l'API Web, ajoutez un itinéraire API Web en tant que:

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 à partir de 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 effectuer plusieurs publications , il est simple, créer plusieurs méthodes d'action et décorer avec l'attribut [HttpPost]. Utilisez le [ActionName ("MyAction")] pour attribuer des noms personnalisés, etc. viendra à jQuery au quatrième point ci-dessous

Troisièmement, tout d'abord, la publication de plusieurs types SIMPLE en une seule action n'est pas possible. De plus, il existe un format spécial pour publier même un seul type simple (à part passer le paramètre dans la chaîne de requête ou le style REST). C'est le point qui m'a fait me cogner la tête avec les clients Rest (comme Fiddler et l'extension cliente Advanced REST de Chrome) et chasser sur le Web pendant près de 5 heures lorsque finalement, l'URL suivante s'est avérée utile. Citera le contenu pertinent pour le lien pourrait devenir mort!

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

PS: Vous avez remarqué la syntaxe particulière ?

http://forums.asp.net/t/1883467.aspx?The+received+value+is+null+when+I+try+to+Post+to+my+Web+Api

Quoi qu'il en soit, revenons sur cette histoire. Passons à autre chose:

Quatrièmement, la publication de types complexes via jQuery, bien sûr, $ .ajax () va rapidement entrer dans le rôle:

Disons que la méthode d'action accepte un objet Person 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 à:

[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 !! À votre santé!

Vaibhav
la source
2
Je semble toucher ce problème tous les quelques mois, la plupart du temps je le résous finalement, mais cette fois j'ai abandonné. Aucun des conseils ci-dessus ne le résout pour moi, j'ai donc décidé de le classer comme approche. S'il est si difficile de bien faire les choses, pourquoi s'embêter? 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. Terminé. Il a fallu probablement 30 secondes pour le résoudre de la manière "difficile" après avoir essayé pendant environ une heure pour le résoudre de manière "facile". Je ne suis pas folle de l'approche, mais y a-t-il un problème fondamental avec elle?
Kinetic
PS: Dans WebApi2, nous pouvons maintenant utiliser des décorateurs d'itinéraire. Cette question est donc principalement abordée. asp.net/web-api/overview/web-api-routing-and-actions/…
Vaibhav
2
Voudrais ajouter une observation. Parfois, la raison pour laquelle la liaison de modèle échoue (null) du côté WebAPI, lors du passage d'un type complexe (ex: DTO), est qu'une ou plusieurs propriétés du modèle seront incompatibles (ou ne parviendront pas à analyser). Par exemple. Une propriété Guid se voit attribuer un GUID non valide. Dans ce cas, essayez d'utiliser les valeurs par défaut / vide pour toutes les propriétés de l'objet et réessayez.
Vaibhav
10

Je viens de jouer avec ça et j'ai découvert un résultat assez étrange. Supposons que vous ayez 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 sûr de ce qui se passe ici, mais cela ressemble à un bogue (fonctionnalité?) Dans le cadre. Vraisemblablement, les différentes méthodes de liaison appellent différents «adaptateurs», et bien que 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.

Andy
la source
6
Propriétés vs champs est pourquoi c'est différent. Les propriétés sont la meilleure pratique. Ce que vous appelez des propriétés dans ce premier exemple sont en fait des champs. Lorsque vous leur mettez un get / set, ils ont alors un champ de sauvegarde créé automatiquement qui en fait des propriétés.
paqogomez
C'est tellement vrai et étrange. Les classes normales avec uniquement des champs ne se lieront pas aux publications de formulaire, mais les propriétés le seront. BTW: N'explique toujours pas pourquoi c'est le cas ...? Je peux seulement deviner que la logique interne ne liera que les données JSON aux champs et formera les données de publication aux propriétés, et c'est tout simplement ...?
James Wilkins
1
C'est le cas car le code ne recherche que les propriétés. Étant donné que l' utilisation des champs publics est pas les meilleures pratiques, l'équipe MS a décidé de ne pas laisser pas les meilleurs scénarios de pratique, assez bonne raison pour mon humble avis.
Erik Philips
1

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

  • contentType: 'application / json'

Ci-dessous est le code donner jquery pour faire un appel ajax post à l'api web asp.net:

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

Dilip Nannaware
la source
2
dataType n'est pas requis.
Erik Philips
0

Assurez-vous que votre service WebAPI attend un objet fortement typé avec une structure qui correspond au JSON que vous transmettez. Et assurez-vous de stringifier le JSON que vous POSTEZ.

Voici mon JavaScript (en 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";
}
scottyj
la source
0

Code suivant pour renvoyer des données au format json, au lieu de xml -Web API 2: -

Mettez la ligne suivante dans le fichier Global.asax

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
UJS
la source
0
@model MVCClient.Models.ProductDetails

@{
    ViewBag.Title = "ProductDetails";
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript">

    $(document).ready(function () {
        $("#Save").click(function () {
            var ProductDetails = new Object();
            ProductDetails.ProductName =  $("#txt_productName").val();
            ProductDetails.ProductDetail = $("#txt_desc").val();
            ProductDetails.Price= $("#txt_price").val();
            $.ajax({
                url: "http://localhost:24481/api/Product/addProduct",
                type: "Post",
                dataType:'JSON',
                data:ProductDetails, 

                success: function (data) {
                    alert('Updated Successfully');
                    //window.location.href = "../Index";
                },
                error: function (msg) { alert(msg); }
            });
        });
    });
    </script>
<h2>ProductDetails</h2>

<form id="form1" method="post">
    <fieldset>
        <legend>ProductDetails</legend>


        <div class="editor-label">
            @Html.LabelFor(model => model.ProductName)
        </div>
        <div class="editor-field">

            <input id="txt_productName" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.ProductName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ProductDetail)
        </div>
        <div class="editor-field">

            <input id="txt_desc" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.ProductDetail)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">

            <input id="txt_price" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.Price)
        </div>



        <p>
            <input id="Save" type="button" value="Create" />
        </p>
    </fieldset>

</form>
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>

</form>



@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
Debendra Dash
la source
0

1) Dans votre côté client, vous pouvez envoyer votre demande http.post en chaîne comme ci-dessous

var IndexInfo = JSON.stringify(this.scope.IndexTree);
this.$http.post('../../../api/EvaluationProcess/InsertEvaluationProcessInputType', "'" + IndexInfo + "'" ).then((response: any) => {}

2) Ensuite, dans votre contrôleur Web API, vous pouvez le désérialiser

public ApiResponce InsertEvaluationProcessInputType([FromBody]string IndexInfo)
    {
var des = (ApiReceivedListOfObjects<TempDistributedIndex>)Newtonsoft.Json.JsonConvert.DeserializeObject(DecryptedProcessInfo, typeof(ApiReceivedListOfObjects<TempDistributedIndex>));}

3) Votre classe ApiReceivedListOfObjects devrait être comme ci-dessous

public class ApiReceivedListOfObjects<T>
    {
        public List<T> element { get; set; }

    }

4) assurez-vous que votre chaîne sérialisée (IndexInfo ici) devient comme la structure ci-dessous avant la commande JsonConvert.DeserializeObject à l'étape 2

var resp = @"
    {
        ""element"": [
        {
            ""A"": ""A Jones"",
            ""B"": ""500015763""
        },
        {
            ""A"": ""B Smith"",
            ""B"": ""504986213""
        },
        {
            ""A"": ""C Brown"",
            ""B"": ""509034361""
        }
        ]
    }";
Mahdi ghafoorian
la source