Como passar dados do POST da Json para o método Web API como um objeto?

A aplicação ASP.NET MVC4 Web API define o método post para salvar o cliente. O cliente é passado em formato json no corpo de requisição POST. O parâmetro do cliente no método POST contém valores nulos para as propriedades.

Como corrigir isso para que os dados lançados passem como objeto do cliente ?

Se possível Content-Type: application/x-www-form-urlencoded deve ser usado, já que não sei como mudá-lo no método javascript que publica o formulário.

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

Pedido:

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"}
Solução

EDIT : 31/10/2017

O mesmo código/abordagem irá funcionar para **Asp.Net Core 2.0*** também. A maior diferença é que, no asp.net core, tanto os controladores web api como os controladores Mvc são fundidos em um único modelo de controlador. Então seu tipo de retorno pode ser IActionResult' ou uma das implementações's (Ex :OkObjectResult')


Use

contentType:"application/json"

Você precisa utilizar o método `JSON.stringify' para convertê-lo para JSON string quando você o envia,

E o modelo de fichário ligará os dados da json ao seu objeto de classe.

O código abaixo vai funcionar bem (testado)

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

**Resultado***

A propriedade contentType diz ao servidor que estamos a enviar os dados em formato JSON. Como nós enviamos uma estrutura de dados JSON, a encadernação do modelo acontecerá corretamente.

Se você inspecionar os cabeçalhos ajax request's, você pode ver que o valor Content-Type está definido como application/json.

Se você não especificar o contentType explicitamente, ele utilizará o tipo de conteúdo padrão que é application/x-www-form-urlencoded;


Editar em Novembro de 2015 para abordar outras possíveis questões levantadas nos comentários

Lançamento de um objeto complexo

Deixe's dizer que você tem uma classe de modelo de visão complexa como parâmetro do seu método de ação de api web como este

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

e o seu ponto final da web api é 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;
    }
}

Na altura desta escrita, ASP.NET MVC 6 é a última versão estável e no MVC6, tanto os controladores de api Web como os controladores MVC são herdados da classe base Microsoft.AspNet.Mvc.Controller.

Para enviar dados para o método do lado do cliente, o código abaixo deve funcionar bem

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

A encadernação de modelos funciona para algumas propriedades, mas não para todas! Porquê?

Se você não decorar o parâmetro do método web api com o atributo [FromBody]

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

E envie o modelo (objeto javascript bruto, não em formato JSON) sem especificar o valor da propriedade contentType

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

A encadernação do modelo funcionará para as propriedades planas no modelo, não para as propriedades onde o tipo é complexo/outro tipo. No nosso caso, as propriedades Id' eName' estarão devidamente vinculadas ao parâmetro m', mas a propriedadeTags' será uma lista vazia.

O mesmo problema ocorrerá se você estiver utilizando a versão curta, $.post que utilizará o Content-Type padrão ao enviar o pedido.

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

Trabalhar com o POST na webapi pode ser complicado! Gostaria de acrescentar à já correcta resposta...

Vai focar especificamente no PÓS, pois lidar com o GET é trivial. Eu não'acho que muitos estariam procurando por aí para resolver um problema com GET com webapis. De qualquer forma...

Se a sua pergunta é - No MVC Web Api, como... - Usar nomes de métodos de ação personalizados que não os verbos HTTP genéricos? - Realizar múltiplos posts? - Postar múltiplos tipos simples? - Postar tipos complexos via jQuery?

Então, as seguintes soluções podem ajudar:

Primeiro, para usar Custom Action Methods in Web API, adicione uma rota de api web como:

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

E então você pode criar métodos de ação como:

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

Agora, dispare o seguinte jQuery a partir do console do seu 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 performar múltiplos posts, É simples, criar múltiplos métodos de ação e decorar com o [HttpPost] attrib. Use o [ActionName("MyAction")] para atribuir nomes personalizados, etc. Vai chegar a jQuery no quarto ponto abaixo

Terceiro, Primeiro de tudo, não é possível lançar múltiplos SIMPLE tipos em uma única ação. Além disso, existe um formato especial para postar até mesmo um single tipo simples (além de passar o parâmetro na query string ou estilo REST). Este foi o ponto que me fez bater a cabeça com os clientes Rest (como Fiddler e Chrome's Advanced REST client extension) e caçar na web por quase 5 horas quando eventualmente, o seguinte URL provou ser de ajuda. Vai citar o conteúdo relevante para o link pode ficar morto!

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: Notou a sintaxe peculiar?

http://forums.asp.net/t/1883467.aspx?The+recebido+valor+valor+é+nulo+quando+I+entrada+para+Pós+para+my+Web+Api

Seja como for, vamos ultrapassar essa história. Continuando:

Quarto, **postando tipos complexos*** via jQuery, ofcourse, $.ajax() vai entrar prontamente no papel:

Digamos que o método de ação aceita um objeto Pessoa que tem um id e um nome. Então, a partir do 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) }
});

E a acção vai parecer como:

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

Tudo isso, funcionou para mim!! Saúde!

Comentários (3)

I'acabei de brincar com isto e descobri um resultado bastante estranho. Digamos que você tem propriedades públicas na sua classe em C# como esta:

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

então você deve fazer o truque JSON.stringify como sugerido por Shyju e chamá-lo assim:

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

No entanto, se você definir getters e setters em sua classe como este:

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

então você pode chamá-lo muito mais simples:

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

Isto utiliza o cabeçalho HTTP:

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

I'não sei bem o que'está acontecendo aqui mas parece um bug (recurso?) no framework. Presumivelmente os diferentes métodos de ligação estão chamando diferentes "adaptadores", e enquanto o adaptador para aplicação/json trabalha com propriedades públicas, o para dados codificados de formulário não't.

Mas não faço ideia qual seria considerada a melhor prática.

Comentários (4)