La aplicación ASP.NET MVC4 Web API define el método post para guardar el cliente. El cliente se pasa en formato json en el cuerpo de la solicitud POST. El parámetro del cliente en el método post contiene valores nulos para las propiedades.
¿Cómo arreglar esto para que los datos publicados se pasen como objeto cliente?
Si es posible Content-Type: application/x-www-form-urlencoded debería usarse ya que no sé cómo cambiarlo en el método javascript que publica el formulario.
Controlador:
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; }
}
Solicitud:
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
El mismo código/enfoque funcionará para Asp.Net Core 2.0 también. La principal diferencia es que, en asp.net core, tanto los controladores web api como los controladores Mvc están fusionados en un único modelo de controlador. Así que su tipo de retorno podría ser IActionResult
o una de sus implementaciones (Ej: OkObjectResult
)
Utilice
contentType:"application/json"
Es necesario utilizar el método JSON.stringify
para convertirlo en cadena JSON cuando lo envíes,
Y el modelo binder vinculará los datos json a su objeto de clase.
El siguiente código funcionará bien (probado)
$(function () {
var customer = {contact_name :"Scott",company_name:"HP"};
$.ajax({
type: "POST",
data :JSON.stringify(customer),
url: "api/Customer",
contentType: "application/json"
});
});
Resultado
introduzca la descripción de la imagen aquí
La propiedad contentType
indica al servidor que estamos enviando los datos en formato JSON. Dado que enviamos una estructura de datos JSON, la vinculación del modelo se realizará correctamente.
Si inspeccionas las cabeceras de la petición ajax, puedes ver que el valor de Content-Type
está establecido como application/json
.
Si no se especifica contentType explícitamente, se utilizará el tipo de contenido por defecto que es application/x-www-form-urlencoded;
.
Edición de noviembre de 2015 para abordar otros posibles problemas planteados en los comentarios
Digamos que usted tiene una clase de modelo de vista compleja como su parámetro de método de acción de la api web así
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;}
}
y tu punto final de la web api es como
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;
}
}
*En el momento de escribir esto, ASP.NET MVC 6 es la última versión estable y en MVC6, tanto los controladores de la api web como los controladores MVC heredan de la clase base Microsoft.AspNet.Mvc.Controller
.
Para enviar datos al método desde el lado del cliente, el siguiente código debería funcionar bien
//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 :)
});
Si no se decora el parámetro del método de la api web con el atributo [FromBody]
.
[HttpPost]
public CreateUserViewModel Save(CreateUserViewModel m)
{
return m;
}
Y envías el modelo (objeto javascript en bruto, no en formato JSON) sin especificar el valor de la propiedad contentType
$.ajax({
type: "POST",
data: model,
url: "../product/save"
}).done(function (res) {
console.log('res', res);
});
La vinculación del modelo funcionará para las propiedades planas en el modelo, no las propiedades donde el tipo es complejo/otro tipo. En nuestro caso, las propiedades Id
y Name
se vincularán correctamente al parámetro m
, pero la propiedad Tags
será una lista vacía.
El mismo problema ocurrirá si se utiliza la versión corta, $.post
que utilizará el Content-Type por defecto al enviar la petición.
$.post("../product/save", model, function (res) {
//res contains the markup returned by the partial view
console.log('res', res);
});
Trabajar con POST en webapi puede ser complicado. Me gustaría añadir a la respuesta ya correcta..
Se centrará específicamente en POST como tratar con GET es trivial. No creo que muchos busquen resolver un problema con GET con webapis. De todos modos...
Si tu pregunta es - En MVC Web Api, ¿cómo - Usar nombres de métodos de acción personalizados que no sean los verbos HTTP genéricos? - ¿Realizar múltiples posts? - ¿Publicar múltiples tipos simples? - ¿Publicar tipos complejos a través de jQuery?
Entonces las siguientes soluciones pueden ayudar:
En primer lugar, para utilizar Métodos de acción personalizados en la API web, añada una ruta api web como:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}");
}
Y entonces usted puede crear métodos de acción como:
[HttpPost]
public string TestMethod([FromBody]string value)
{
return "Hello from http post web api controller: " + value;
}
Ahora, dispara el siguiente jQuery desde la consola de tu navegador
$.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) }
});
Segundo, para realizar múltiples posts, es sencillo, crea múltiples métodos de acción y decóralos con el atributo [HttpPost]. Utilice el [ActionName("MyAction")] para asignar nombres personalizados, etc. Llegará a jQuery en el cuarto punto siguiente
Tercero, En primer lugar, la publicación de múltiples SIMPLE tipos en una sola acción no es posible. Además, hay un formato especial para publicar incluso un tipo simple (aparte de pasar el parámetro en la cadena de consulta o estilo REST). Este fue el punto que me tuvo dándole vueltas a la cabeza con clientes Rest (como Fiddler y la extensión de cliente REST avanzado de Chrome) y cazando por la web durante casi 5 horas cuando finalmente, la siguiente URL resultó ser de ayuda. Citaré el contenido relevante porque el enlace podría estar muerto.
Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"[email protected]"}
PD: ¿Has notado la sintaxis peculiar?
http://forums.asp.net/t/1883467.aspx?The+valor+recibido+es+nulo+cuando+intento+postear+en+mi+Web+Api
En fin, superemos esa historia. Sigamos adelante:
Cuarto, posting de tipos complejos vía jQuery, por supuesto, $.ajax() va a entrar puntualmente en el papel:
Digamos que el método de acción acepta un objeto Persona que tiene un id y un nombre. Entonces, desde 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) }
});
Y la acción se verá como
[HttpPost]
public string TestMethod(Person person)
{
return "Hello from http post web api controller: " + person.Name;
}
¡¡Todo lo anterior, me ha funcionado!! ¡Saludos!
He estado jugando con esto y he descubierto un resultado bastante extraño. Digamos que usted tiene propiedades públicas en su clase en C # como este:
public class Customer
{
public string contact_name;
public string company_name;
}
entonces debes hacer el truco de JSON.stringify como sugiere Shyju y llamarlo así:
var customer = {contact_name :"Scott",company_name:"HP"};
$.ajax({
type: "POST",
data :JSON.stringify(customer),
url: "api/Customer",
contentType: "application/json"
});
Sin embargo, si defines getters y setters en tu clase así
public class Customer
{
public string contact_name { get; set; }
public string company_name { get; set; }
}
entonces usted puede llamar mucho más simple:
$.ajax({
type: "POST",
data :customer,
url: "api/Customer"
});
Esto utiliza la cabecera HTTP:
Content-Type:application/x-www-form-urlencoded
No estoy muy seguro de lo que está sucediendo aquí, pero parece un error (característica?) en el marco. Es de suponer que los diferentes métodos de vinculación están llamando a diferentes "adaptadores", y mientras que el adaptador para la aplicación/json uno trabaja con las propiedades públicas, el de los datos codificados de forma doesn't.
Sin embargo, no tengo ni idea de cuál se considera la mejor práctica.