Come passare i dati POST json al metodo API Web come oggetto?


304

L'applicazione API Web ASP.NET MVC4 definisce il metodo post per salvare il cliente. Il cliente viene passato in formato json nel corpo della richiesta POST. Il parametro cliente nel metodo post contiene valori null per le proprietà.

Come risolvere questo problema in modo che i dati pubblicati vengano passati come oggetto cliente?

Se possibile Content-Type: application / x-www-form-urlencoded dovrebbe essere usato dato che non so come cambiarlo nel metodo javascript che forma i post.

controller:

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

Richiesta:

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

Risposte:


525

MODIFICA : 31/10/2017

Lo stesso codice / approccio funzionerà anche per Asp.Net Core 2.0 . La differenza principale è che, nel nucleo di asp.net, sia i controller Web API che i controller Mvc sono uniti in un unico modello di controller. Così il vostro tipo di ritorno potrebbe essere IActionResulto una delle sue applicazione (Es: OkObjectResult)


Uso

contentType:"application/json"

Devi utilizzare il JSON.stringifymetodo per convertirlo in stringa JSON quando lo invii,

E il raccoglitore di modelli assocerà i dati json all'oggetto classe.

Il codice seguente funzionerà bene (testato)

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

Risultato

inserisci qui la descrizione dell'immagine

contentTypeLa proprietà dice al server che stiamo inviando i dati in formato JSON. Da quando abbiamo inviato una struttura di dati JSON, l'associazione del modello avverrà correttamente.

Se controlli le intestazioni della richiesta Ajax, puoi vedere che il Content-Typevalore è impostato come application/json.

Se non si specifica esplicitamente contentType, verrà utilizzato il tipo di contenuto predefinito che è application/x-www-form-urlencoded;


Modifica a novembre 2015 per affrontare altre possibili questioni sollevate nei commenti

Pubblicazione di un oggetto complesso

Supponiamo che tu abbia una complessa classe del modello di vista come parametro del metodo di azione dell'API Web come questo

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

e il tuo punto finale dell'API Web è simile

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

Al momento della stesura di questo documento, ASP.NET MVC 6 è l'ultima versione stabile e in MVC6, sia i controller Web API che i controller MVC ereditano dalla Microsoft.AspNet.Mvc.Controllerclasse base.

Per inviare dati al metodo dal lato client, il codice seguente dovrebbe funzionare correttamente

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

L'associazione del modello funziona per alcune proprietà, ma non per tutte! Perché ?

Se non si decora il parametro del metodo api web con [FromBody]attributo

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

E invia il modello (oggetto javascript non elaborato, non in formato JSON) senza specificare il valore della proprietà contentType

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

L'associazione del modello funzionerà per le proprietà piatte sul modello, non per quelle in cui il tipo è complesso / un altro tipo. Nel nostro caso, Ide le Nameproprietà saranno correttamente associate al parametro m, ma la Tagsproprietà sarà un elenco vuoto.

Lo stesso problema si verificherà se si utilizza la versione breve, $.postche utilizzerà il tipo di contenuto predefinito quando si invia la richiesta.

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

4
Non sono sicuro di quello che ho fatto, ma sono tornato questa mattina e sono tornato sulla stessa barca. L'oggetto è nullo nel controller. eccoci di nuovo lol
Grayson,

1
assicurati che il tipo di contenuto sia scritto "Content-Type: application / json" mentre usi il violinista. Saluti!
ioWint

1
Mi hai semplicemente risolto una giornata di lavoro !!! Questa minuscola funzione "JSON.stringify (data)" l'ha creata!
Gil Allen,

1
Tieni presente che se lo fai (cambia l'intestazione Content-Type) e stai facendo una richiesta CORS, jQuery inizierà ad aggiungere richieste OPTIONS di verifica preliminare prima del tuo POST che il server dovrà gestire.
Arbitro

1
A causa del problema con tipi complessi, penso che abbia l'abitudine di specificare solo 'contentType:' application / json; ' e json stringono l'oggetto js e quindi non è necessario utilizzare l'attributo [FromBody].
BornToCode

69

Lavorare con POST in webapi può essere complicato! Vorrei aggiungere la risposta già corretta ..

Si concentrerà in particolare sul POST poiché gestire GET è banale. Non penso che molti sarebbero alla ricerca di un problema con GET con webapis. In ogni modo ..

Se la tua domanda è - In MVC Web Api, come - - Utilizzare nomi di metodi di azione personalizzati diversi dai verbi HTTP generici? - Eseguire più post? - Pubblicare più tipi semplici? - Pubblicare tipi complessi tramite jQuery?

Quindi le seguenti soluzioni possono aiutare:

Innanzitutto, per utilizzare i metodi di azione personalizzati nell'API Web, aggiungere una route API Web come:

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

E quindi puoi creare metodi di azione come:

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

Ora, avvia il seguente jQuery dalla tua console del browser

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

In secondo luogo, per eseguire più post , è semplice, creare più metodi di azione e decorare con l'attributo [HttpPost]. Utilizzare [ActionName ("MyAction")] per assegnare nomi personalizzati, ecc. Verrà su jQuery nel quarto punto seguente

Terzo, Innanzitutto, non è possibile pubblicare più tipi SEMPLICI in una sola azione. Inoltre, esiste un formato speciale per pubblicare anche un solo tipo semplice (oltre a passare il parametro nella stringa di query o nello stile REST). Questo è stato il punto che mi ha fatto battere la testa con Rest Clients (come Fiddler e l'estensione client Advanced REST di Chrome) e cercare sul Web per quasi 5 ore quando alla fine il seguente URL si è rivelato di aiuto. Citerà il contenuto pertinente per il link potrebbe diventare morto!

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: hai notato la sintassi peculiare ?

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

Comunque, cerchiamo di superare quella storia. Andare avanti:

In quarto luogo, la pubblicazione di tipi complessi tramite jQuery, ofcourse, $ .ajax () entrerà rapidamente nel ruolo:

Diciamo che il metodo action accetta un oggetto Person che ha un id e un nome. Quindi, da 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) }
});

E l'azione sarà simile a:

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

Tutto quanto sopra, ha funzionato per me !! Saluti!


2
Mi sembra di risolvere questo problema ogni pochi mesi, il più delle volte alla fine lo risolvo, ma questa volta mi sono arreso. Nessuno dei suggerimenti di cui sopra lo risolve per me, quindi ho deciso di considerare questo come approccio. Se è così difficile avere ragione, perché preoccuparsi? È comunque solo una comodità: prendi il contenuto come una stringa e usa newtonsoft per trasformarlo. Fatto. Ci sono voluti probabilmente 30 secondi per risolverlo nel modo "difficile" dopo aver provato per circa un'ora a risolverlo nel modo "facile". Non sono pazzo per l'approccio, ma c'è un problema fondamentale con esso?
Cinetico,

PS: In WebApi2, ora possiamo usare Route Decorator. Quindi questo problema è principalmente affrontato. asp.net/web-api/overview/web-api-routing-and-actions/…
Vaibhav

2
Vorrei aggiungere un'osservazione. A volte, il motivo per cui l'associazione del modello fallisce (null) sul lato WebAPI, quando si passa un tipo complesso (es: DTO), è che una o più proprietà nel modello saranno incompatibili (o non riescono ad analizzare). Per esempio. Una proprietà Guid a cui viene assegnato un GUID non valido. In questo caso, provare a utilizzare i valori predefiniti / vuoti per tutte le proprietà dell'oggetto e riprovare.
Vaibhav,

10

Ci ho appena giocato e ho scoperto un risultato piuttosto strano. Supponi di avere proprietà pubbliche nella tua classe in C # in questo modo:

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

allora devi fare il trucco JSON.stringify come suggerito da Shyju e chiamarlo così:

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

Tuttavia, se definisci getter e setter della tua classe in questo modo:

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

allora puoi chiamarlo molto più semplicemente:

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

Questo utilizza l'intestazione HTTP:

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

Non sono sicuro di cosa stia succedendo qui, ma sembra un bug (funzione?) Nel framework. Presumibilmente i diversi metodi di associazione chiamano diversi "adattatori" e mentre l'adattatore per application / json funziona con proprietà pubbliche, quello per i dati codificati in forma no.

Non ho idea di quale sarebbe comunque considerata la migliore pratica.


6
Proprietà vs campi è il motivo per cui è diverso. Le proprietà sono le migliori pratiche. Ciò che chiamate proprietà in quel primo esempio sono in realtà campi. Quando si inserisce un get / set su di essi, hanno quindi un campo di supporto creato automaticamente che li rende proprietà.
paqogomez,

Questo è così vero e strano. Le classi normali con solo campi non si legheranno ai moduli, ma le proprietà lo faranno. BTW: Ancora non spiega perché questo è il caso ...? Posso solo supporre che la logica interna legherà solo i dati JSON ai campi e formerà i dati dei post nelle proprietà, e questo è semplicemente ...?
James Wilkins,

1
È il caso perché il codice cerca solo proprietà. Dal momento che l'utilizzo dei campi pubblici non è una buona pratica, il team di MS ha deciso di non consentire scenari non di buona pratica, motivo abbastanza valido per IMHO.
Erik Philips,

1

Utilizzare JSON.stringify () per ottenere la stringa in formato JSON, assicurarsi che durante la chiamata AJAX si passino gli attributi sotto indicati:

  • contentType: 'application / json'

Di seguito è riportato il codice dare jquery per effettuare la chiamata ajax post a 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
dataType non è richiesto.
Erik Philips,

0

Assicurati che il tuo servizio WebAPI si aspetti un oggetto fortemente tipizzato con una struttura che corrisponda al JSON che stai passando. E assicurati di stringere il codice JSON che stai postando.

Ecco il mio JavaScript (usando 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 ()
        {
        });

Ed ecco il mio controller WebAPI:

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

0

Il codice seguente per restituire i dati nel formato json, anziché l'Xml -Web API 2: -

Inserire la seguente riga nel file Global.asax

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

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


0

1) Nel lato client puoi inviarti una richiesta http.post in stringa come di seguito

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

2) Quindi nel tuo controller API Web puoi deserializzarlo

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

3) La tua classe ApiReceivedListOfObjects dovrebbe essere come di seguito

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

    }

4) assicurarsi che la stringa serializzata (qui IndexInfo) diventi come la struttura sottostante prima del comando JsonConvert.DeserializeObject al passaggio 2

var resp = @"
    {
        ""element"": [
        {
            ""A"": ""A Jones"",
            ""B"": ""500015763""
        },
        {
            ""A"": ""B Smith"",
            ""B"": ""504986213""
        },
        {
            ""A"": ""C Brown"",
            ""B"": ""509034361""
        }
        ]
    }";
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.