Jak przekazać dane json POST do metody Web API jako obiekt?

Aplikacja ASP.NET MVC4 Web API definiuje metodę post do zapisania klienta. Klient jest przekazywany w formacie json w treści żądania POST. Parametr Customer w metodzie post zawiera wartości null dla właściwości.

Jak to naprawić, aby dane były przekazywane jako obiekt klienta?

Jeśli to możliwe Content-Type: application/x-www-form-urlencoded powinien być użyty, ponieważ nie wiem jak to zmienić w metodzie javascript, która wysyła formularz.

Kontroler:

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

Żądanie:

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"}
Rozwiązanie

EDIT : 31/10/2017

Ten sam kod / podejście będzie działać dla Asp.Net Core 2.0 również. Główną różnicą jest, W asp.net core, zarówno kontrolery web api, jak i kontrolery Mvc są łączone razem w pojedynczy model kontrolera. Więc twój typ zwrotu może być IActionResult lub jedną z jego'implementacji (Ex :OkObjectResult).


Użyj

contentType:"application/json"

Musisz użyć metody JSON.stringify, aby przekonwertować go na ciąg JSON, kiedy go wyślesz,

A model binder powiąże dane json z obiektem twojej klasy.

Poniższy kod będzie działał dobrze (przetestowany)

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

Wynik

Właściwość contentType mówi serwerowi, że wysyłamy dane w formacie JSON. Ponieważ wysłaliśmy strukturę danych JSON, wiązanie modelu odbędzie się poprawnie.

Jeśli sprawdzisz nagłówki żądania ajax's, możesz zobaczyć, że wartość Content-Type jest ustawiona jako application/json.

Jeśli nie określisz contentType, zostanie użyty domyślny typ zawartości, który jest application/x-www-form-urlencoded;.


Edyt z listopada 2015, aby rozwiązać inne możliwe problemy poruszone w komentarzach

Wysyłanie obiektu złożonego

Let's say you have a complex view model class as your web api action method parameter like this

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

a twój punkt końcowy web api jest jak

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

*W czasie pisania tego tekstu, ASP.NET MVC 6 jest najnowszą stabilną wersją i w MVC6, zarówno kontrolery Web api jak i kontrolery MVC dziedziczą z klasy bazowej Microsoft.AspNet.Mvc.Controller.

Aby wysłać dane do metody od strony klienta, poniższy kod powinien działać dobrze

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

Wiązanie modelu działa dla niektórych właściwości, ale nie dla wszystkich ! Dlaczego ?

Jeśli nie udekorujesz parametru metody web api atrybutem [FromBody].

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

I wyślesz model (surowy obiekt javascript, nie w formacie JSON) bez określania wartości właściwości contentType

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

Wiązanie modelu będzie działać dla płaskich właściwości na modelu, a nie właściwości, gdzie typ jest złożony/innego typu. W naszym przypadku, właściwości Id i Name będą poprawnie powiązane z parametrem m, ale właściwość Tags będzie pustą listą.

Ten sam problem wystąpi jeśli używasz krótkiej wersji, $.post, która użyje domyślnego Content-Type podczas wysyłania żądania.

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

Praca z POST w webapi może być trudna! Chciałbym dodać do już poprawnej odpowiedzi...

Skupię się szczególnie na POST, ponieważ radzenie sobie z GET jest banalne. Nie sądzę, aby wielu szukało rozwiązania problemu z GET za pomocą webapi. Tak czy inaczej...

Jeśli twoje pytanie brzmi - W MVC Web Api, jak - Użyj niestandardowych nazw metod działania innych niż ogólne czasowniki HTTP? - Wykonywać wiele postów? - Publikować wiele prostych typów? - Publikuj złożone typy za pomocą jQuery?

Następnie następujące rozwiązania mogą pomóc:

Po pierwsze, aby użyć Custom Action Methods w Web API, dodaj trasę web api jako:

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

A następnie możesz utworzyć metody akcji takie jak:

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

Teraz, z konsoli przeglądarki, odpal poniższe 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) }
});

Po drugie, aby wykonać wiele postów, Jest to proste, utwórz wiele metod akcji i udekoruj atrybutem [HttpPost]. Użyj [ActionName("MyAction")], aby przypisać niestandardowe nazwy itp. Przejdziemy do jQuery w czwartym punkcie poniżej

Po trzecie, Po pierwsze, delegowanie wielu SIMPLE typów w pojedynczej akcji nie jest możliwe. Co więcej, istnieje specjalny format, aby opublikować nawet pojedynczy prosty typ (poza przekazaniem parametru w łańcuchu zapytania lub w stylu REST). To był punkt, który kazał mi walić głową w Rest Clients (jak Fiddler i Chrome's Advanced REST client extension) i polować w sieci przez prawie 5 godzin, kiedy w końcu następujący adres URL okazał się pomocny. Zacytuje odpowiednią treść, ponieważ link może okazać się martwy!

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: Zauważyłeś specyficzną składnię?

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

Tak czy inaczej, skończmy z tą historią. Przechodząc dalej:

Po czwarte, postowanie typów złożonych za pomocą jQuery, oczywiście w roli tej szybko pojawi się $.ajax():

Powiedzmy, że metoda akcji przyjmuje obiekt Person, który ma id i nazwę. Tak więc, z 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) }
});

A akcja będzie wyglądała tak:

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

Wszystkie z powyższych, pracował dla mnie!!! Dzięki!

Komentarze (3)

I've just been playing with this and discovered a rather strange result. Powiedzmy, że masz publiczne właściwości na swojej klasie w C#, takie jak to:

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

to musisz zrobić sztuczkę JSON.stringify, jak zasugerował Shyju i nazwać to w ten sposób:

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

Jednakże, jeśli zdefiniujesz gettery i setery na swojej klasie w ten sposób:

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

wtedy możesz nazwać to znacznie prościej:

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

Wykorzystuje to nagłówek HTTP:

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

Nie jestem do końca pewien, co się tutaj dzieje, ale wygląda to na błąd (cechę?) w frameworku. Przypuszczalnie różne metody wiązania wywołują różne "adaptery", i podczas gdy adapter dla application/json działa z publicznymi właściwościami, ten dla danych zakodowanych w formularzu nie.

Nie mam pojęcia, co byłoby uważane za najlepszą praktykę, chociaż.

Komentarze (4)