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"}
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
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<TagViewModel> 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 :)
});
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);
});
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":"[email protected]"}
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!
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ż.