web-dev-qa-db-fra.com

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

L'application API Web ASP.NET MVC4 définit la méthode d'envoi pour enregistrer le client. Le client est transmis au format JSON dans le corps de la demande POST. Le paramètre client dans la méthode post contient des valeurs null pour les propriétés.

Comment résoudre ce problème afin que les données publiées passent 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 avec la méthode javascript qui poste 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"}
284
Andrus

EDIT: 31/10/2017

Le même code/la même approche fonctionnera pour Asp.Net Core 2.. La principale différence réside dans le fait qu’en asp.net core, les contrôleurs API Web et les contrôleurs MVC sont fusionnés dans un modèle de contrôleur unique. Donc, votre type de retour peut être IActionResult ou l'une de ses implémentations (Ex: OkObjectResult)


Utilisation

contentType:"application/json"

Vous devez utiliser la méthode JSON.stringify pour la convertir en chaîne JSON lors de son envoi,

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

enter image description here

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

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

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


Éditez novembre 2015 pour résoudre d'autres problèmes éventuels 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 Web api, 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 votre point final Web api est comme

public class ProductController : Controller
{
    [HttpPost]
    public CreateUserViewMode 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 de la rédaction de ce document, ASP.NET MVC 6 est la dernière version stable. Dans MVC6, les contrôleurs Web api et les contrôleurs MVC héritent de la classe de base Microsoft.AspNet.Mvc.Controller.

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 toutes! Pourquoi ?

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

[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 à plat du modèle, pas pour les propriétés dont le type est complexe/un autre type. Dans notre cas, les propriétés Id et Name seront correctement liées au paramètre m, mais la propriété Tags sera une liste vide.

Le même problème se produira si vous utilisez la version abrégée, $.post, qui utilisera le type de contenu 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);
});
502
Shyju

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

Se concentrera spécifiquement sur POST car le traitement de GET est trivial. Je ne pense pas que beaucoup chercheraient à résoudre un problème lié à GET with 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 posts? - Publier plusieurs types simples? - Publier des types complexes via jQuery?

Ensuite, les solutions suivantes peuvent aider:

Tout d’abord, pour utiliser les méthodes d’action personnalisées dans l’API Web, ajoutez une route d’API Web en tant que:

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

Et ensuite, vous pouvez créer des méthodes d'action telles que:

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

Troisièmement, il est tout d'abord impossible de publier plusieurs types SIMPLES dans une seule action. De plus, il existe un format spécial permettant de publier même un type simple (à part le passage du paramètre dans la chaîne de requête ou dans le style REST). C'est ce qui m'a poussé à me cogner la tête avec des clients restants (comme Fiddler et l'extension client avancée REST de Chrome) de Chrome et à fouiller sur le Web pendant près de 5 heures. Citerons le contenu pertinent pour que le lien devienne 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++++++++++

Quoi qu'il en soit, laissez-nous sur cette histoire. Passer à:

Quatrièmement, en affichant des types complexes via jQuery, ofcourse, $ .ajax () va rapidement occuper le rôle suivant:

Supposons que la méthode d'action accepte un objet Person ayant un identifiant et un nom. Donc, 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é!

68
Vaibhav

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

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

alors vous devez faire le tour 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 les accesseurs sur votre classe, procédez comme suit:

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

Ceci 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 bogue (fonctionnalité?) Dans le framework. Vraisemblablement, les différentes méthodes de liaison appellent différents "adaptateurs", et bien que l'adaptateur pour application/json fonctionne avec des propriétés publiques, celui des données codées en formulaire ne fonctionne pas.

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

10
Andy

Utilisez le JSON.stringify () pour obtenir la chaîne au format JSON, assurez-vous que, lorsque vous appelez AJAX, vous transmettez les attributs mentionnés ci-dessous:

  • contentType: 'application/json'
  • dataType: 'json'

Vous trouverez ci-dessous le code jQuery à utiliser pour passer 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',
    dataType: 'json',
    data: product,
    success: function (data, status, xhr) {
        alert('Success!');
    },
    error: function (xhr, status, error) {
        alert('Update Error occurred - ' + error);
    }
});
1
Dilip Nannaware

Assurez-vous que votre service WebAPI attend un objet fortement typé avec une structure correspondant au JSON que vous transmettez. Et assurez-vous de bien définir le code JSON que vous publiez.

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";
}
0
scott janson

1) Dans votre côté client, vous pouvez vous envoyer la requête 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 d’API Web, 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""
        }
        ]
    }";
0
Mahdi ghafoorian

Code suivant pour renvoyer des données au format JSON au lieu de l'API xml -Web 2: -

Place 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);
0
UJS
@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")
}
0
Debendra Dash

Microsoft en a donné un bon exemple:

https://docs.Microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-1

D'abord valider la demande

if (ModelState.IsValid)

et que d'utiliser les données sérialisées.

Content = new StringContent(update.Status)

Ici 'Status' est un champ dans le type complexe. La sérialisation est effectuée par .NET, inutile de vous inquiéter pour cela.

0
FrankyHollywood