ASP .NET Web API -  Passando múltiplos parâmetros usando jQuery


Neste artigo veremos passar múltiplos parãmetros para uma API ASP .NET MVC 5 usando jQuery.

As Web APIs criadas via ASP .NET MVC 5 possuem algumas restrições para mapear variáveis de formulário POST para parâmetros simples de um método Action da API. A Web API não lida com vários valores de conteúdo postados.

Você só pode postar um único valor de conteúdo em um método Action da API e neste artigo veremos as diferentes maneiras de passar vários parâmetros para o método Action de uma Web API.

Suponha que você tenha o seguinte método na sua Web API definida em um controller ProdutosController:

    public HttpResponseMessage PostProduto(int Id, string Nome, int Estoque, decimal Preco)
    {
           //seu código
    }

E você esta tentando fazer a seguinte chamada usando jQuery:

$.ajax({
 url: 'api/produtos',
 type: 'POST',
 data: { Id: 2012, Nome: 'teste', Estoque: 55, Preco: 19.5 },
 dataType: 'json',
 success: function (data) {
     alert(data);
}
 
});

Infelizmente, a WEB API não vai poder tratar esta requisição, e, você vai obter um erro.

Para obter sucesso você tem que passar os parâmetros usando uma query string, assim :

$.ajax({
 url: 'api/produtos?Id=2012
&Nome=teste&Estoque=55&Preco=19.5',
 type: 'POST',               
 dataType: 'json',
 success: function (data) {
     alert(data);
  }
 
 });

Funcionar, funciona, mas não é uma boa solução e não é aplicável a objetos complexos.

Então, vejamos a seguir outras maneiras de fazer isso...

Usando o Model Binding

O model binding permite mapear dados de uma requisição HTTP para um modelo,em um processo de criar objetos .NET usando os dados enviados pelo navegador em uma requisição HTTP.

O model binding funciona como uma ponte projetada entre os métodos HTTP e os métodos Action da API. Neste contexto o POST, GET, DELETE, PUT, etc. são transferidos automaticamente para um modelo de dados definido.

Como exemplo vamos criar um modelo representado pela classe Produto:

   public class Produto
   {
       public int Id { get; set; }
       public string Nome { get; set; }
       public string Categoria { get; set; }
       public decimal Preco { get; set; }
   }

Agora vamos usar esse modelo de domínio em um controlador ProdutosController como um argumento no método Action PostProduto:

 public HttpResponseMessage PostProduto(Produto item)
 {
     item = repository.Add(item);
     var response = Request.CreateResponse<Produto>(HttpStatusCode.Created, item);
     string uri = Url.Link("DefaultApi", new { id = item.Id });
     response.Headers.Location = new Uri(uri);
     return response;
 }

Para fazer uma chamada para este método via jQuery podemos fazer assim:

$.ajax({
 url: 'api/produtos',
 type: 'POST',
 data: { Id: 2012, Nome: 'teste', Estoque: 44, Preco: 19.5 },
 dataType: 'json',
 success: function (data) {
         alert(data);
  }
});

Agora vai funcionar, graças ao model binding.

Podemos obter o mesmo resultado de outra forma fazendo assim:

var produto = { Id: 2012, Nome: 'teste', Estoque: 44, Preco: 19.5 }

$.ajax({

 url: 'api/produtos',
 type: 'POST',
 data: JSON.stringify(produto),
 dataType: 'json',
 success: function (data) {
         alert(data);
  }
});

Nota: JSON.stringify() é uma função do objeto JSON que criará uma string do objeto dentro do padrão adotado pelo JSON.

Assim também vai funcionar graças ao model binding.

Usando o binding de parâmetros customizado

A Web API da ASP.NET permite criar uma vinculação de parâmetro personalizada para estender a funcionalidade e permite que você intercepte o processamento de parâmetros individuais.

a- Usando JObject

Agora, a ASP .NET WEB usa o JSON.NET como serializador JSON. Assim, você pode usar a classe JObject para receber um resultado JSON dinâmico, convertê-lo corretamente ou analisá-lo na forma de objetos fortemente tipados.

Veja o exemplo abaixo:

       public HttpResponseMessage PostProduto(JObject data)
       {
           dynamic json = data;
           Produto item = new Produto() {
               Id = json.Id,
               Nome = json.Nome,
               Estoque = json.Estoque,
               Preco =json.Preco
           };
           item = repository.Add(item);
           var response = Request.CreateResponse<Produto>(HttpStatusCode.Created, item);
           string uri = Url.Link("DefaultApi", new { id = item.Id });
           response.Headers.Location = new Uri(uri);
           return response;
       }

Ou de uma forma mais direta podemos tratar os dados em uma classe fortemente tipada:

       public HttpResponseMessage PostProduto(JObject data)
       {
           Produto item = data.ToObject<Produto>(); 
 
           item = repository.Add(item);
           var response = Request.CreateResponse<Produto>(HttpStatusCode.Created, item);
           string uri = Url.Link("DefaultApi", new { id = item.Id });
           response.Headers.Location = new Uri(uri);
           return response;
       }

b- Usando FormDataCollection

Outra opção é você definir o argumento do tipo FormDataCollection e ler cada parâmetro um por um manualmente usando os métodos .Get() ou .GetValues() (para valores de seleção múltipla).

Veja o exemplo a seguir para os dados:  data : { Id: 2019 , Nome= 'teste, Estoque : 44, Preco: 19.5 }

Podemos fazer assim:

       public HttpResponseMessage PostProduto(FormDataCollection data)
       {
           Produto item = new Produto() {
               Id = Convert.ToInt32(data.Get("Id")),
               Nome = data.Get("Nome"),
               Estoque = Convert.ToInt32(data.Get("Estoque")),
               Preco = Convert.ToDecimal(data.Get("Preco"))
           };
           item = repository.Add(item);
           var response = Request.CreateResponse<Produto>(HttpStatusCode.Created, item);
           string uri = Url.Link("DefaultApi", new { id = item.Id });
           response.Headers.Location = new Uri(uri);
           return response;
       }

E se você quiser retornar múltiplos parâmetros ?

A primeira coisa a fazer é usar um Model com todos os parâmetros e retornar sua instância.

Mas não podemos criar um modelo para cada tipo de retorno a cada requisição.

Se todos os parâmetros tiverem o mesmo tipo de dados, podemos criar e retornar uma coleção. Mas os parâmetros podem ter tipos de dados diferentes.

E ai ?

Neste caso a solução mais simples é usar o tipo de retorno dynamic.

       public dynamic GetProdutos()
       {
           var produtos = repository.GetAll() as IEnumerable<Produto>;
           return new
           {
              Produtos = produtos,
              Criteria = "definido"
           };
       }
 

Vimos assim algumas maneiras de passar mais de um parâmetro em requisições para uma API.

"E não temais os que matam o corpo e não podem matar a alma; temei antes aquele que pode fazer perecer no inferno a alma e o corpo."
Mateus 10:28

Referências:


José Carlos Macoratti