Πώς να περάσετε δεδομένα json POST στη μέθοδο Web API ως αντικείμενο;

Η εφαρμογή ASP.NET MVC4 Web API ορίζει τη μέθοδο post για την αποθήκευση του πελάτη. Ο πελάτης περνάει σε μορφή json στο σώμα της αίτησης POST. Η παράμετρος Customer στη μέθοδο post περιέχει μηδενικές τιμές για τις ιδιότητες.

Πώς να το διορθώσετε αυτό, ώστε τα δεδομένα της αποστολής να περάσουν ως αντικείμενο πελάτη ?

Εάν είναι δυνατόν, θα πρέπει να χρησιμοποιηθεί Content-Type: application/x-www-form-urlencoded, δεδομένου ότι δεν ξέρω πώς να το αλλάξω στη μέθοδο javascript που δημοσιεύει τη φόρμα.

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

Αίτημα:

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"}
Λύση

EDIT : 31/10/2017

Ο ίδιος κώδικας/προσέγγιση θα λειτουργήσει και για το Asp.Net Core 2.0. Η σημαντική διαφορά είναι, Στο asp.net core, τόσο οι ελεγκτές web api όσο και οι ελεγκτές Mvc συγχωνεύονται μαζί σε ένα ενιαίο μοντέλο ελεγκτή. Έτσι, ο τύπος επιστροφής σας μπορεί να είναι IActionResult ή μία από τις υλοποιήσεις του (π.χ. OkObjectResult).


Χρήση

contentType:"application/json"

Πρέπει να χρησιμοποιήσετε τη μέθοδο JSON.stringify για να το μετατρέψετε σε συμβολοσειρά JSON όταν το στέλνετε,

Και το model binder θα δεσμεύσει τα δεδομένα json στο αντικείμενο της κλάσης σας.

Ο παρακάτω κώδικας θα λειτουργήσει μια χαρά (δοκιμασμένος)

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

Αποτέλεσμα

Η ιδιότητα contentType δηλώνει στο διακομιστή ότι στέλνουμε τα δεδομένα σε μορφή JSON. Εφόσον στείλαμε μια δομή δεδομένων JSON, η σύνδεση μοντέλου θα γίνει σωστά.

Αν επιθεωρήσετε τις επικεφαλίδες του αιτήματος ajax's, μπορείτε να δείτε ότι η τιμή Content-Type έχει οριστεί ως application/json.

Εάν δεν καθορίσετε ρητά το contentType, θα χρησιμοποιηθεί ο προεπιλεγμένος τύπος περιεχομένου που είναι application/x-www-form-urlencoded;


Επεξεργασία τον Νοέμβριο 2015 για να αντιμετωπιστούν άλλα πιθανά ζητήματα που τέθηκαν στα σχόλια

Δημοσίευση σύνθετου αντικειμένου

Ας πούμε ότι έχετε μια σύνθετη κλάση μοντέλου προβολής ως παράμετρο της μεθόδου δράσης web api όπως αυτή

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

και το τελικό σημείο του web api σας είναι όπως

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

*Το ASP.NET MVC 6 είναι η τελευταία σταθερή έκδοση και στο MVC6, τόσο οι ελεγκτές Web api όσο και οι ελεγκτές MVC κληρονομούν από την κλάση βάσης Microsoft.AspNet.Mvc.Controller.

Για να στείλετε δεδομένα στη μέθοδο από την πλευρά του πελάτη, ο παρακάτω κώδικας θα πρέπει να λειτουργεί κανονικά

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

Η δέσμευση μοντέλου λειτουργεί για ορισμένες ιδιότητες, αλλά όχι για όλες ! Γιατί;

Εάν δεν διακοσμήσετε την παράμετρο της μεθόδου web api με την ιδιότητα [FromBody]

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

Και στέλνετε το μοντέλο (ακατέργαστο αντικείμενο javascript, όχι σε μορφή JSON) χωρίς να καθορίσετε την τιμή της ιδιότητας contentType

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

Η δέσμευση μοντέλου θα λειτουργήσει για τις επίπεδες ιδιότητες του μοντέλου, όχι για τις ιδιότητες όπου ο τύπος είναι σύνθετος/άλλος τύπος. Στην περίπτωσή μας, οι ιδιότητες Id και Name θα δεσμευτούν σωστά στην παράμετρο m, αλλά η ιδιότητα Tags θα είναι μια κενή λίστα.

Το ίδιο πρόβλημα θα εμφανιστεί αν χρησιμοποιείτε τη σύντομη έκδοση, $.post η οποία θα χρησιμοποιήσει το προεπιλεγμένο Content-Type κατά την αποστολή της αίτησης.

$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});
Σχόλια (17)

Η εργασία με POST στο webapi μπορεί να είναι δύσκολη! Θα ήθελα να προσθέσω στην ήδη σωστή απάντηση ..

Θα επικεντρωθούμε ειδικά στο POST, καθώς η αντιμετώπιση του GET είναι τετριμμένη. Δεν νομίζω ότι πολλοί θα έψαχναν για την επίλυση ενός προβλήματος με το GET με το webapis. Εν πάση περιπτώσει...

Εάν η ερώτησή σας είναι - Σε MVC Web Api, πώς να - - Χρησιμοποιήστε προσαρμοσμένα ονόματα μεθόδων δράσης εκτός από τα γενικά ρήματα HTTP; - Να εκτελείτε πολλαπλές δημοσιεύσεις; - Να δημοσιεύετε πολλαπλούς απλούς τύπους; - Δημοσίευση σύνθετων τύπων μέσω jQuery;

Τότε οι παρακάτω λύσεις μπορεί να σας βοηθήσουν:

Πρώτον, για να χρησιμοποιήσετε Προσαρμοσμένες Μεθόδους δράσης στο Web API, προσθέστε μια διαδρομή web api ως εξής:

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

Και στη συνέχεια μπορείτε να δημιουργήσετε μεθόδους δράσης όπως:

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

Τώρα, πυροδοτήστε την ακόλουθη jQuery από την κονσόλα του προγράμματος περιήγησης

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

Δεύτερον, για να εκτελέσετε πολλαπλές δημοσιεύσεις, Είναι απλό, δημιουργήστε πολλαπλές μεθόδους δράσης και διακοσμήστε με το χαρακτηριστικό [HttpPost]. Χρησιμοποιήστε το [ActionName("MyAction")] για να εκχωρήσετε προσαρμοσμένα ονόματα κ.λπ. Θα αναφερθούμε στην jQuery στο τέταρτο σημείο παρακάτω.

Τρίτο, Πρώτα απ' όλα, η αποστολή πολλαπλών ΣΥΜΠΛΗΡΩΜΑΤΙΚΩΝ τύπων σε μία μόνο ενέργεια δεν είναι δυνατή. Επιπλέον, υπάρχει μια ειδική μορφή για την ανάρτηση ακόμη και ενός απλού τύπου (εκτός από το πέρασμα της παραμέτρου στο query string ή στο στυλ REST). Αυτό ήταν το σημείο που με έκανε να χτυπάω το κεφάλι μου με Rest Clients (όπως το Fiddler και το Chrome's Advanced REST client extension) και να κυνηγάω στο διαδίκτυο για σχεδόν 5 ώρες, όταν τελικά, το ακόλουθο URL αποδείχθηκε ότι βοήθησε. Θα παραθέσω το σχετικό περιεχόμενο γιατί ο σύνδεσμος μπορεί να αποδειχθεί νεκρός!

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

ΥΓ: Παρατηρήσατε την ιδιαίτερη σύνταξη;

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

Τέλος πάντων, ας ξεπεράσουμε αυτή την ιστορία. Συνεχίζουμε:

Τέταρτον, η δημοσίευση σύνθετων τύπων μέσω της jQuery, φυσικά, η $.ajax() θα έρθει αμέσως στο ρόλο:

Ας πούμε ότι η μέθοδος action δέχεται ένα αντικείμενο Person το οποίο έχει ένα id και ένα όνομα. Έτσι, από 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) }
});

Και η ενέργεια θα μοιάζει με:

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

Όλα τα παραπάνω λειτούργησαν για μένα!! Στην υγειά μας!

Σχόλια (3)

Μόλις έπαιζα με αυτό και ανακάλυψα ένα μάλλον περίεργο αποτέλεσμα. Ας πούμε ότι έχετε δημόσιες ιδιότητες στην κλάση σας σε C# ως εξής:

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

τότε θα πρέπει να κάνετε το κόλπο JSON.stringify όπως προτείνει ο Shyju και να το καλέσετε ως εξής:

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

Ωστόσο, αν ορίσετε getters και setters στην κλάση σας ως εξής:

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

τότε μπορείτε να την καλέσετε πολύ πιο απλά:

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

Αυτό χρησιμοποιεί την επικεφαλίδα HTTP:

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

Δεν είμαι απόλυτα σίγουρος τι συμβαίνει εδώ, αλλά μοιάζει με σφάλμα (χαρακτηριστικό;) στο πλαίσιο. Πιθανότατα οι διαφορετικές μέθοδοι δέσμευσης καλούν διαφορετικούς "προσαρμογείς", και ενώ ο προσαρμογέας για application/json λειτουργεί με δημόσιες ιδιότητες, αυτός για δεδομένα κωδικοποιημένα με φόρμα δεν το κάνει'όχι.

Ωστόσο, δεν έχω ιδέα για το ποια θα θεωρούνταν βέλτιστη πρακτική.

Σχόλια (4)