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

304

O aplicativo ASP.NET MVC4 Web API define o método de postagem para salvar o cliente. O cliente é passado no formato json no corpo da solicitação POST. O parâmetro do cliente no método post contém valores nulos para propriedades.

Como corrigir isso para que os dados postados passem como objeto do cliente?

Se possível, tipo de conteúdo: application / x-www-form-urlencoded deve ser usado, pois não sei como alterá-lo no método javascript que posta 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; }
     }

Solicitação:

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

Respostas:

525

EDIT : 31/10/2017

O mesmo código / abordagem também funcionará para o Asp.Net Core 2.0 . A principal diferença é que, no núcleo do asp.net, os controladores da API da Web e os controladores Mvc são mesclados no modelo de controlador único. Portanto, o seu tipo de retorno pode ser IActionResultou um de sua implementação (Ex: OkObjectResult)


Usar

contentType:"application/json"

Você precisa usar o JSON.stringifymétodo para convertê-lo em string JSON ao enviá-lo,

E o fichário do modelo vinculará os dados json ao seu objeto de classe.

O código abaixo 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

insira a descrição da imagem aqui

contentTypeA propriedade informa ao servidor que estamos enviando os dados no formato JSON. Como enviamos uma estrutura de dados JSON, a ligação do modelo ocorrerá corretamente.

Se você inspecionar os cabeçalhos da solicitação ajax, poderá ver que o Content-Typevalor está definido como application/json.

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


Edite em novembro de 2015 para resolver outros possíveis problemas levantados nos comentários

Postando um objeto complexo

Digamos que você tenha uma classe de modelo de exibição complexa como seu parâmetro do método de ação da API da web, como este

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

e seu ponto final da API da Web é 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;
    }
}

No momento da redação deste artigo, o ASP.NET MVC 6 é a versão estável mais recente e, no MVC6, os controladores de API da Web e os MVC estão herdando da Microsoft.AspNet.Mvc.Controllerclasse base.

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 ligação de modelo funciona para algumas propriedades, mas não para todas! Por quê ?

Se você não decorar o parâmetro do método API da Web com o [FromBody]atributo

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

E envie o modelo (objeto javascript bruto, não no 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 ligação do modelo funcionará para as propriedades simples do modelo, não para as propriedades em que o tipo é complexo / outro tipo. No nosso caso, Ide as Namepropriedades serão corretamente vinculadas ao parâmetro m, mas a Tagspropriedade será uma lista vazia.

O mesmo problema ocorrerá se você estiver usando a versão curta, $.postque usará o Tipo de Conteúdo padrão ao enviar a solicitação.

$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});
Shyju
fonte
4
Não tenho certeza do que fiz, mas voltei hoje de manhã e voltei ao mesmo barco. O objeto é nulo no controlador. aqui vamos nós de novo lol
Grayson
1
verifique se o tipo de conteúdo está escrito "Content-Type: application / json" enquanto você estiver usando o violinista. Felicidades!
ioWint 7/01/15
1
Você simplesmente me resolveu um dia de trabalho !!! Esta pequena função "JSON.stringify (data)" foi criada!
Gil Allen
1
Lembre-se de que, se você fizer isso (alterar o cabeçalho do tipo de conteúdo) e fizer uma solicitação CORS, o jQuery começará a adicionar solicitações OPTION de comprovação antes do POST, com as quais o servidor precisará lidar.
Arbiter
1
Por causa do problema com tipos complexos, acho que há um hábito de especificar 'contentType:' application / json; ' e json stringify o objeto js e, em seguida, não há necessidade de usar o atributo [FromBody].
BornToCode
69

Trabalhar com POST em webapi pode ser complicado! Gostaria de adicionar à resposta já correta ..

O foco será especificamente no POST, pois lidar com GET é trivial. Eu não acho que muitos estariam procurando ao redor para resolver um problema com GET com webapis. Enfim..

Se sua pergunta for - No MVC Web Api, como- - Usar nomes de métodos de ação personalizados que não sejam os verbos HTTP genéricos? - Executar várias postagens? - Publicar vários tipos simples? - Publique tipos complexos via jQuery?

Em seguida, as seguintes soluções podem ajudar:

Primeiro, para usar métodos de ação personalizados na API da Web, adicione uma rota de API da 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, ative o seguinte jQuery no console do 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 executar várias postagens , é simples, crie vários métodos de ação e decore com o atributo [HttpPost]. Use o [ActionName ("MyAction")] para atribuir nomes personalizados, etc. Chegará ao jQuery no quarto ponto abaixo

Terceiro, primeiro de tudo, não é possível postar vários tipos SIMPLE em uma única ação. Além disso, existe um formato especial para postar até um único tipo simples (além de passar o parâmetro na string de consulta ou no estilo REST). Esse foi o ponto que me fez bater de cabeça com os clientes Rest (como o Fiddler e a extensão de cliente REST avançada do Chrome) e procurar na web por quase 5 horas, quando, eventualmente, o URL a seguir se mostrou útil. 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":"[email protected]"}

PS: Percebeu a sintaxe peculiar ?

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

De qualquer forma, vamos superar essa história. Se movendo:

Em quarto lugar, a publicação de tipos complexos via jQuery, é claro, $ .ajax () assumirá prontamente a função:

Digamos que o método de ação aceite um objeto Pessoa que tenha um ID e um nome. Então, 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 ação será semelhante a:

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

Todos os itens acima, funcionaram para mim !! Felicidades!

Vaibhav
fonte
2
Parece que eu bato nesse problema a cada poucos meses, na maioria das vezes resolvo, mas dessa vez desisti. Nenhuma das dicas acima resolve isso para mim, então decidi colocar isso como uma abordagem. Se é tão difícil de acertar, por que se preocupar? De qualquer forma, é apenas uma conveniência - basta pegar o conteúdo como uma string e usar a newtonsoft para transformá-lo. Feito. Demorou provavelmente 30 segundos para resolvê-lo da maneira "difícil" depois de tentar por cerca de uma hora resolvê-la da maneira "fácil". Eu não sou louco por essa abordagem, mas há um problema fundamental com ela?
Kinetic
PS: No WebApi2, agora podemos usar Route Decorators. Portanto, esse problema é tratado principalmente. asp.net/web-api/overview/web-api-routing-and-actions/…
Vaibhav
2
Gostaria de adicionar uma observação. Às vezes, o motivo da falha na ligação do modelo (nula) no lado da WebAPI, ao passar um tipo complexo (ex: DTO), é que uma ou mais propriedades no modelo serão incompatíveis (ou falham ao analisar). Por exemplo. Uma propriedade Guid sendo atribuída a um GUID inválido. Nesse caso, tente usar os valores padrão / vazios para todas as propriedades do objeto e tente novamente.
Vaibhav
10

Acabei de brincar com isso e descobri um resultado bastante estranho. Digamos que você tenha propriedades públicas em sua classe em C # assim:

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

você deve executar o truque JSON.stringify, conforme 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, desta forma:

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

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

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

Isso usa o cabeçalho HTTP:

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

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

Não tenho idéia do que seria considerado uma boa prática.

Andy
fonte
6
Propriedades vs campos é por que é diferente. Propriedades são a melhor prática. O que você chama de propriedades nesse primeiro exemplo são, de fato, campos. Quando você coloca um get / set neles, eles têm um campo de apoio criado automaticamente que os torna propriedades.
Paqogomez 15/05
Isso é tão verdadeiro e estranho. Classes normais com apenas campos não serão vinculadas para formar postagens, mas propriedades serão vinculadas. BTW: Ainda não explica por que esse é o caso ...? Só posso supor que a lógica interna vinculará apenas os dados JSON aos campos e formará os dados de postagem nas propriedades, e é simplesmente isso ...?
27416 James Wilkins
1
É o caso, porque o código procura apenas propriedades. Como o uso de campos públicos não é uma prática recomendada, a equipe do MS decidiu não permitir cenários de não prática recomendada, muito bom motivo para IMHO.
Erik Philips
1

Use o JSON.stringify () para obter a sequência no formato JSON, assegure-se de que, ao fazer a chamada AJAX, você passe abaixo dos atributos mencionados:

  • contentType: 'application / json'

Abaixo está o código give jquery para fazer ajax post call para asp.net web api:

var product =
    JSON.stringify({
        productGroup: "Fablet",
        productId: 1,
        productName: "Lumia 1525 64 GB",
        sellingPrice: 700
    });

$.ajax({
    URL: 'http://localhost/api/Products',
    type: 'POST',
    contentType: 'application/json',
    data: product,
    success: function (data, status, xhr) {
        alert('Success!');
    },
    error: function (xhr, status, error) {
        alert('Update Error occurred - ' + error);
    }
});

Dilip Nannaware
fonte
2
dataType não é necessário.
Erik Philips
0

Certifique-se de que seu serviço WebAPI esteja esperando um objeto fortemente tipado com uma estrutura que corresponda ao JSON que você está passando. E certifique-se de especificar o JSON que você está postando.

Aqui está o meu JavaScript (usando o AngluarJS):

$scope.updateUserActivity = function (_objuserActivity) {
        $http
        ({
            method: 'post',
            url: 'your url here',
            headers: { 'Content-Type': 'application/json'},
            data: JSON.stringify(_objuserActivity)
        })
        .then(function (response)
        {
            alert("success");
        })
        .catch(function (response)
        {
            alert("failure");
        })
        .finally(function ()
        {
        });

E aqui está o meu controlador WebAPI:

[HttpPost]
[AcceptVerbs("POST")]
public string POSTMe([FromBody]Models.UserActivity _activity)
{
    return "hello";
}
scottyj
fonte
0

Código a seguir para retornar dados no formato json, em vez da xml -Web API 2: -

Coloque a seguinte linha no arquivo Global.asax

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
UJS
fonte
0
@model MVCClient.Models.ProductDetails

@{
    ViewBag.Title = "ProductDetails";
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript">

    $(document).ready(function () {
        $("#Save").click(function () {
            var ProductDetails = new Object();
            ProductDetails.ProductName =  $("#txt_productName").val();
            ProductDetails.ProductDetail = $("#txt_desc").val();
            ProductDetails.Price= $("#txt_price").val();
            $.ajax({
                url: "http://localhost:24481/api/Product/addProduct",
                type: "Post",
                dataType:'JSON',
                data:ProductDetails, 

                success: function (data) {
                    alert('Updated Successfully');
                    //window.location.href = "../Index";
                },
                error: function (msg) { alert(msg); }
            });
        });
    });
    </script>
<h2>ProductDetails</h2>

<form id="form1" method="post">
    <fieldset>
        <legend>ProductDetails</legend>


        <div class="editor-label">
            @Html.LabelFor(model => model.ProductName)
        </div>
        <div class="editor-field">

            <input id="txt_productName" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.ProductName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ProductDetail)
        </div>
        <div class="editor-field">

            <input id="txt_desc" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.ProductDetail)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">

            <input id="txt_price" type="text" name="fname">
            @Html.ValidationMessageFor(model => model.Price)
        </div>



        <p>
            <input id="Save" type="button" value="Create" />
        </p>
    </fieldset>

</form>
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>

</form>



@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
Debendra Dash
fonte
0

1) No lado do cliente, você pode enviar uma solicitação http.post na string como abaixo

var IndexInfo = JSON.stringify(this.scope.IndexTree);
this.$http.post('../../../api/EvaluationProcess/InsertEvaluationProcessInputType', "'" + IndexInfo + "'" ).then((response: any) => {}

2) Em seguida, no seu controlador de API da web, você pode desserializá-lo

public ApiResponce InsertEvaluationProcessInputType([FromBody]string IndexInfo)
    {
var des = (ApiReceivedListOfObjects<TempDistributedIndex>)Newtonsoft.Json.JsonConvert.DeserializeObject(DecryptedProcessInfo, typeof(ApiReceivedListOfObjects<TempDistributedIndex>));}

3) Sua classe ApiReceivedListOfObjects deve ser como abaixo

public class ApiReceivedListOfObjects<T>
    {
        public List<T> element { get; set; }

    }

4) verifique se a string serializada (IndexInfo aqui) fica como abaixo da estrutura antes do comando JsonConvert.DeserializeObject na etapa 2

var resp = @"
    {
        ""element"": [
        {
            ""A"": ""A Jones"",
            ""B"": ""500015763""
        },
        {
            ""A"": ""B Smith"",
            ""B"": ""504986213""
        },
        {
            ""A"": ""C Brown"",
            ""B"": ""509034361""
        }
        ]
    }";
Mahdi ghafoorian
fonte