Wie werden JSON-POST-Daten als Objekt an die Web-API-Methode übergeben?

304

Die ASP.NET MVC4-Web-API-Anwendung definiert die Post-Methode zum Speichern des Kunden. Der Kunde wird im json-Format im POST-Anforderungshauptteil übergeben. Der Kundenparameter in der Post-Methode enthält Nullwerte für Eigenschaften.

Wie kann dies behoben werden, damit gebuchte Daten als Kundenobjekt übergeben werden?

Wenn möglich, sollte Content-Type: application / x-www-form-urlencoded verwendet werden, da ich nicht weiß, wie ich es in der Javascript-Methode ändern soll, die das Formular veröffentlicht.

Regler:

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

Anfrage:

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
quelle

Antworten:

525

EDIT : 31.10.2017

Der gleiche Code / Ansatz funktioniert auch für Asp.Net Core 2.0 . Der Hauptunterschied besteht darin, dass im asp.net-Kern sowohl Web-API-Controller als auch Mvc-Controller zu einem einzigen Controller-Modell zusammengeführt werden. Also Ihr könnte Rückgabetyp sein IActionResultoder eine Implementierung von ihm ist (Beispiel: OkObjectResult)


Verwenden

contentType:"application/json"

Sie müssen die JSON.stringifyMethode verwenden, um sie beim Senden in eine JSON-Zeichenfolge zu konvertieren.

Der Modellordner bindet die JSON-Daten an Ihr Klassenobjekt.

Der folgende Code funktioniert einwandfrei (getestet)

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

Ergebnis

Geben Sie hier die Bildbeschreibung ein

contentTypeDie Eigenschaft teilt dem Server mit, dass die Daten im JSON-Format gesendet werden. Da wir eine JSON-Datenstruktur gesendet haben, erfolgt die Modellbindung ordnungsgemäß.

Wenn Sie die Header der Ajax-Anforderung überprüfen, sehen Sie, dass der Content-TypeWert auf festgelegt ist application/json.

Wenn Sie contentType nicht explizit angeben, wird der Standardinhaltstyp verwendet application/x-www-form-urlencoded;


Bearbeiten Sie im November 2015, um andere mögliche Probleme zu beheben, die in Kommentaren angesprochen werden

Ein komplexes Objekt buchen

Angenommen, Sie haben eine komplexe Ansichtsmodellklasse als solchen Parameter für die Web-API-Aktionsmethode

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

und Ihr Web-API-Endpunkt ist wie

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

Zum Zeitpunkt dieses Schreibens ist ASP.NET MVC 6 die neueste stabile Version. In MVC6 erben sowohl Web-API-Controller als auch MVC-Controller von der Microsoft.AspNet.Mvc.ControllerBasisklasse.

Um Daten von der Clientseite an die Methode zu senden, sollte der folgende Code einwandfrei funktionieren

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

Die Modellbindung funktioniert für einige Eigenschaften, aber nicht für alle! Warum ?

Wenn Sie den Web-API-Methodenparameter nicht mit [FromBody]Attribut dekorieren

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

Senden Sie das Modell (unformatiertes Javascript-Objekt, nicht im JSON-Format), ohne den Eigenschaftswert contentType anzugeben

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

Die Modellbindung funktioniert für die flachen Eigenschaften des Modells, nicht für die Eigenschaften, bei denen der Typ komplex ist / ein anderer Typ. In unserem Fall Idund NameEigenschaften ordnungsgemäß an die Parameter gebunden werden m, aber die TagsEigenschaft wird eine leere Liste sein.

Das gleiche Problem tritt auf, wenn Sie die Kurzversion verwenden, $.postdie beim Senden der Anforderung den Standardinhaltstyp verwendet.

$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});
Shyju
quelle
4
Ich bin mir nicht sicher, was ich getan habe, aber ich bin heute Morgen zurückgekommen und wieder im selben Boot. Objekt ist in der Steuerung null. hier gehen wir wieder lol
Grayson
1
Stellen Sie sicher, dass der Inhaltstyp "Content-Type: application / json" geschrieben ist, während Sie Fiddler verwenden. Prost!
ioWint
1
Du hast mir einfach einen Arbeitstag gelöst !!! Diese winzige Funktion "JSON.stringify (data)" hat es geschafft!
Gil Allen
1
Beachten Sie, dass jQuery in diesem Fall (Ändern des Content-Type-Headers) und beim Erstellen einer CORS-Anforderung vor Ihrem POST Preflight-OPTIONS-Anforderungen hinzufügt, die der Server verarbeiten muss.
Schiedsrichter
1
Aufgrund des Problems mit komplexen Typen ist es meiner Meinung nach eine Gewohnheit, nur 'contentType:' application / json; ' und json stringifizieren das js-Objekt, und dann muss das Attribut [FromBody] nicht verwendet werden.
BornToCode
69

Die Arbeit mit POST in Webapi kann schwierig sein! Möchte zur bereits richtigen Antwort hinzufügen ..

Wird sich speziell auf POST konzentrieren, da der Umgang mit GET trivial ist. Ich glaube nicht, dass viele nach einer Lösung für ein Problem mit GET mit Webapis suchen würden. Sowieso..

Wenn Ihre Frage lautet: - Verwenden Sie in MVC Web Api andere benutzerdefinierte Aktionsmethoden als die generischen HTTP-Verben. - Mehrere Posts durchführen? - Mehrere einfache Typen posten? - Komplexe Typen über jQuery posten?

Dann können die folgenden Lösungen helfen:

Fügen Sie zunächst eine Web-API-Route hinzu , um benutzerdefinierte Aktionsmethoden in der Web-API zu verwenden :

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

Und dann können Sie Aktionsmethoden erstellen wie:

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

Starten Sie nun die folgende jQuery über Ihre Browserkonsole

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

Zweitens, um mehrere Beiträge auszuführen , ist es einfach, mehrere Aktionsmethoden zu erstellen und mit dem Attribut [HttpPost] zu dekorieren. Verwenden Sie den [Aktionsnamen ("MyAction")], um benutzerdefinierte Namen usw. zuzuweisen. Wird im vierten Punkt unten zu jQuery kommen

Drittens ist es zunächst nicht möglich , mehrere EINFACHE Typen in einer einzigen Aktion zu veröffentlichen. Darüber hinaus gibt es ein spezielles Format , um auch nur einen einfachen Typ zu veröffentlichen (abgesehen von der Übergabe des Parameters in der Abfragezeichenfolge oder im REST-Stil). Dies war der Punkt, an dem ich mich mit Rest Clients (wie der Advanced REST-Client-Erweiterung von Fiddler und Chrome) in den Kopf schlug und fast 5 Stunden lang im Internet herumjagte, als sich schließlich die folgende URL als hilfreich erwies. Wird der relevante Inhalt für den Link zitieren könnte tot werden!

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: Haben Sie die eigentümliche Syntax bemerkt ?

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

Wie auch immer, lassen Sie uns über diese Geschichte hinwegkommen. Weiter geht's:

Viertens wird das Posten komplexer Typen über jQuery, natürlich $ .ajax (), sofort in die Rolle kommen:

Angenommen, die Aktionsmethode akzeptiert ein Personenobjekt mit einer ID und einem Namen. Also, aus 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) }
});

Und die Aktion wird aussehen wie:

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

All das hat bei mir funktioniert !! Prost!

Vaibhav
quelle
2
Ich scheine dieses Problem alle paar Monate zu treffen, die meiste Zeit löse ich es schließlich, aber dieses Mal habe ich aufgegeben. Keiner der oben genannten Tipps löst das Problem für mich, daher habe ich beschlossen, dies als Ansatz zu verwenden. Wenn es so schwer ist, richtig zu machen, warum dann? Es ist sowieso nur eine Annehmlichkeit - nehmen Sie den Inhalt einfach als Zeichenfolge und transformieren Sie ihn mit newtonsoft. Getan. Es dauerte wahrscheinlich 30 Sekunden, um es auf "harte" Weise zu lösen, nachdem ich ungefähr eine Stunde lang versucht hatte, es auf "einfache" Weise zu lösen. Ich bin nicht wild auf den Ansatz, aber gibt es ein grundlegendes Problem damit?
Kinetic
PS: In WebApi2 können wir jetzt Routendekorateure verwenden. Daher wird dieses Problem in erster Linie angesprochen. asp.net/web-api/overview/web-api-routing-and-actions/…
Vaibhav
2
Möchte eine Beobachtung hinzufügen. Manchmal ist der Grund dafür, dass die Modellbindung auf der WebAPI-Seite fehlschlägt (null), wenn ein komplexer Typ (z. B. DTO) übergeben wird, dass eine oder mehrere Eigenschaften im Modell nicht kompatibel sind (oder nicht analysiert werden können). Z.B. Einer Guid-Eigenschaft wird eine ungültige GUID zugewiesen. Versuchen Sie in diesem Fall, die Standard- / Leerwerte für alle Objekteigenschaften zu verwenden, und versuchen Sie es erneut.
Vaibhav
10

Ich habe gerade damit gespielt und ein ziemlich seltsames Ergebnis entdeckt. Angenommen, Sie haben öffentliche Eigenschaften für Ihre Klasse in C # wie folgt:

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

Dann müssen Sie den von Shyju vorgeschlagenen Trick JSON.stringify ausführen und ihn folgendermaßen aufrufen:

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

Wenn Sie jedoch Getter und Setter für Ihre Klasse wie folgt definieren:

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

dann kann man es viel einfacher nennen:

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

Dies verwendet den HTTP-Header:

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

Ich bin mir nicht ganz sicher, was hier passiert, aber es sieht aus wie ein Fehler (Feature?) Im Framework. Vermutlich rufen die verschiedenen Bindungsmethoden unterschiedliche "Adapter" auf, und während der Adapter für application / json one mit öffentlichen Eigenschaften arbeitet, funktioniert der Adapter für formularcodierte Daten nicht.

Ich habe jedoch keine Ahnung, welche als Best Practice gelten würde.

Andy
quelle
6
Eigenschaften gegen Felder ist der Grund, warum es anders ist. Eigenschaften sind die beste Vorgehensweise. Was Sie in diesem ersten Beispiel Eigenschaften nennen, sind tatsächlich Felder. Wenn Sie ein Get / Set auf sie setzen, haben sie ein automatisch erstelltes Hintergrundfeld, das sie zu Eigenschaften macht.
Paqogomez
Das ist so wahr und seltsam. Normale Klassen mit nur Feldern werden nicht an Formularbeiträge gebunden, Eigenschaften jedoch. Übrigens: Erklärt immer noch nicht, warum dies der Fall ist ...? Ich kann nur vermuten, dass die interne Logik nur JSON-Daten an Felder bindet und Post-Daten an Eigenschaften bildet, und das war's einfach ...?
James Wilkins
1
Dies ist der Fall, weil der Code nur nach Eigenschaften sucht. Da die Verwendung öffentlicher Felder keine bewährte Methode ist, hat das MS-Team beschlossen, keine bewährten Vorgehensweisen zuzulassen , was meiner Meinung nach ein guter Grund ist.
Erik Philips
1

Verwenden Sie JSON.stringify () , um die Zeichenfolge im JSON-Format abzurufen. Stellen Sie sicher, dass Sie beim Ausführen des AJAX-Aufrufs die folgenden Attribute übergeben:

  • contentType: 'application / json'

Unten finden Sie den JQuery-Code für einen Ajax-Post-Aufruf der 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);
    }
});

Dilip Nannaware
quelle
2
Datentyp ist nicht erforderlich.
Erik Philips
0

Stellen Sie sicher, dass Ihr WebAPI-Dienst ein stark typisiertes Objekt mit einer Struktur erwartet, die dem von Ihnen übergebenen JSON entspricht. Stellen Sie sicher, dass Sie den JSON, den Sie POSTEN, stringifizieren.

Hier ist mein JavaScript (mit 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 ()
        {
        });

Und hier ist mein WebAPI-Controller:

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

Der folgende Code zum Zurückgeben von Daten im JSON-Format anstelle der XML-Web-API 2: -

Fügen Sie die folgende Zeile in die Datei Global.asax ein

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
UJS
quelle
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
quelle
0

1) Auf Ihrer Client-Seite können Sie eine http.post-Anfrage in einer Zeichenfolge wie unten senden

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

2) Dann können Sie es in Ihrem Web-API-Controller deserialisieren

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

3) Ihre ApiReceivedListOfObjects-Klasse sollte wie folgt aussehen

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

    }

4) Stellen Sie sicher, dass Ihre serialisierte Zeichenfolge (IndexInfo hier) wie folgt aussieht, bevor Sie den Befehl JsonConvert.DeserializeObject in Schritt 2 ausführen

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