AngularJS - Há alguma maneira de $ http.post enviar parâmetros de solicitação em vez de JSON?

116

Eu tenho um código antigo que está fazendo uma solicitação AJAX POST por meio do método post do jQuery e se parece com isto:

$.post("/foo/bar", requestData,
    function(responseData)
    {
        //do stuff with response
    }

requestData é apenas um objeto javascript com algumas propriedades básicas de string.

Estou movendo nosso material para usar o Angular e desejo substituir esta chamada por $ http.post. Eu vim com o seguinte:

$http.post("/foo/bar", requestData).success(
    function(responseData) {
        //do stuff with response
    }
});

Quando fiz isso, recebi uma resposta de erro 500 do servidor. Usando o Firebug, descobri que ele enviou o corpo da solicitação assim:

{"param1":"value1","param2":"value2","param3":"value3"}

O jQuery bem-sucedido $.postenvia o corpo assim:

param1=value1&param2=value2&param3=value3

O ponto de extremidade que estou atingindo está esperando parâmetros de solicitação e não JSON. Então, minha pergunta é: há como dizer $http.postpara enviar o objeto javascript como parâmetros de solicitação em vez de JSON? Sim, eu sei que poderia construir a string sozinho a partir do objeto, mas quero saber se o Angular fornece algo para isso fora da caixa.

dnc253
fonte

Respostas:

140

Acho que o paramsparâmetro de configuração não funcionará aqui, pois adiciona a string ao url em vez do corpo, mas para adicionar ao que o Infeligo sugeriu aqui é um exemplo da substituição global de uma transformação padrão (usando o parâmetro jQuery como exemplo para converter os dados para a string param).

Configure a função transformaRequest global:

var app = angular.module('myApp');

app.config(function ($httpProvider) {
    $httpProvider.defaults.transformRequest = function(data){
        if (data === undefined) {
            return data;
        }
        return $.param(data);
    }
});

Dessa forma, todas as chamadas para $ http.post transformarão automaticamente o corpo para o mesmo formato de parâmetro usado pela $.postchamada jQuery .

Observe que você também pode definir o cabeçalho Content-Type por chamada ou globalmente desta forma:

$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';

Exemplo de transformRequest não global por chamada:

    var transform = function(data){
        return $.param(data);
    }

    $http.post("/foo/bar", requestData, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
        transformRequest: transform
    }).success(function(responseData) {
        //do stuff with response
    });
Gloopy
fonte
Eu queria saber se havia algo diferente de ter uma função transformRequest, mas parece que não há. Obrigado por se informar sobre a função jQuery param.
dnc253
O método não global por chamada está funcionando bem para mim, mas ao tentar configurar globalmente via $httpProvider.defaults, e não funcionar, alguma pista sobre isso?
Dfr
1
Configurando o WRT globalmente, também estou tendo problemas. Quando tento fazer isso usando o snippet fornecido aqui, recebo um erro. Cannot read property "jquery" of undefined.Como faço para corrigir isso? PS. As transformações por chamada funcionam.
kshep92
@ kshep92 O que está acontecendo é que a função transformRequest está sendo chamada em uma solicitação sem dados, portanto 'dados' não estão definidos. Eu adicionei um guarda antes de 'return $ .param (data);'. Insira isso como a primeira linha para a função transformRequest: 'if (data === undefined) return data;' Veja a edição que fiz na resposta.
Jere.Jones
1
a partir do Angular 1.4, você pode usar $ httpParamSerializer em vez de jQuery docs.angularjs.org/api/ng/service/$httpParamSerializer
theRemix
21

Se estiver usando Angular> = 1,4 , aqui está a solução mais limpa que descobri que não depende de nada personalizado ou externo:

angular.module('yourModule')
  .config(function ($httpProvider, $httpParamSerializerJQLikeProvider){
    $httpProvider.defaults.transformRequest.unshift($httpParamSerializerJQLikeProvider.$get());
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
});

E então você pode fazer isso em qualquer lugar em seu aplicativo:

$http({
  method: 'POST',
  url: '/requesturl',
  data: {
    param1: 'value1',
    param2: 'value2'
  }
});

E ele serializará corretamente os dados param1=value1&param2=value2e os enviará /requesturlcom o application/x-www-form-urlencoded; charset=utf-8cabeçalho Content-Type, como normalmente é esperado com solicitações POST em terminais.

Saeb Amini
fonte
17

Da documentação do AngularJS:

params - {Object.} - Mapa de strings ou objetos que serão transformados em? key1 = value1 & key2 = value2 após o url. Se o valor não for uma string , ele será JSONified.

Portanto, forneça string como parâmetros. Se você não quiser isso, use transformações. Novamente, a partir da documentação:

Para substituir essas transformações localmente, especifique as funções de transformação como propriedades transformRequest e / ou transformResponse do objeto de configuração. Para substituir globalmente as transformações padrão, substitua as propriedades $ httpProvider.defaults.transformRequest e $ httpProvider.defaults.transformResponse do $ httpProvider.

Consulte a documentação para obter mais detalhes.

Infeligo
fonte
Eu vi os parâmetros na documentação e, como Gloopy menciona, eu preciso deles no corpo, e não na URL. Eu queria saber se havia alguma opção ou algo que estava faltando para fazer os parâmetros em vez de JSON, mas parece que eu só preciso usar a propriedade transformRequest.
dnc253
15

Use a $.paramfunção jQuery para serializar os dados JSON em requestData.

Resumindo, usando um código semelhante ao seu:

$http.post("/foo/bar",
$.param(requestData),
{
    headers:
    {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
}
).success(
    function(responseData) {
        //do stuff with response
    }
});

Para usar isso, você deve incluir jQuery em sua página junto com AngularJS.

Sagar Bhosale
fonte
7

Observe que, a partir do Angular 1.4, você pode serializar os dados do formulário sem usar jQuery.

No app.js:

module.run(function($http, $httpParamSerializerJQLike) {
  $http.defaults.transformRequest.unshift($httpParamSerializerJQLike);
});

Então, em seu controlador:

$http({
    method: 'POST',
    url: myUrl',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    data: myData
});
Thomas Graziani
fonte
Essa resposta é ótima. Ele aborda os 2 problemas principais com Post from Angular. O cabeçalho deve ser definido corretamente e você deve serializar os dados json. Se você não precisa do suporte do IE8, use 1.4+ ou posterior.
mbokil
Acabei de implementar isso e resolve os problemas que estava tendo com a postagem, mas também muda a forma como o patch funciona e parece ter quebrado todos os meus usos de $ http.patch ().
Mike Feltman,
5

Isso pode ser um pouco um hack, mas evitei o problema e converti o json na matriz POST do PHP no lado do servidor:

$_POST = json_decode(file_get_contents('php://input'), true);
TimoSolo
fonte
Já usei esse método, mas odeio; e demorei muito para descobrir por que precisava usar isso.
meconroy
como eu disse - parece hacky. Como a maioria do php;)
TimoSolo
5

Também tenho problemas com a configuração da autenticação http personalizada porque $ resource armazena a solicitação em cache.

Para fazer funcionar, você deve sobrescrever os cabeçalhos existentes fazendo isso

var transformRequest = function(data, headersGetter){
  var headers = headersGetter();
  headers['Authorization'] = 'WSSE profile="UsernameToken"';
  headers['X-WSSE'] = 'UsernameToken ' + nonce
  headers['Content-Type'] = 'application/json';
};

return $resource(
  url,
    {
    },
    {
      query: {
        method: 'POST',
        url: apiURL + '/profile',
        transformRequest: transformRequest,
        params: {userId: '@userId'}
      },
    }
);

Espero ter ajudado alguém. Levei 3 dias para descobrir isso.

Frank marcelo
fonte
Acho que você acabou de me poupar 3 dias de trabalho. Obrigado!!! Ainda estou tentando descobrir se posso interceptar a chamada de solicitação de alguma forma para que possa injetar um cabeçalho personalizado para cada chamada.
marcoseu
4

Modifique os cabeçalhos padrão:

$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8";

Em seguida, use o $.parammétodo JQuery :

var payload = $.param({key: value});
$http.post(targetURL, payload);
Zags
fonte
3
   .controller('pieChartController', ['$scope', '$http', '$httpParamSerializerJQLike', function($scope, $http, $httpParamSerializerJQLike) {
        var data = {
                TimeStamp : "2016-04-25 12:50:00"
        };
        $http({
            method: 'POST',
            url: 'serverutilizationreport',
            headers: {'Content-Type': 'application/x-www-form-urlencoded'},
            data: $httpParamSerializerJQLike(data),
        }).success(function () {});
    }
  ]);
Rohit Luthra
fonte
Na minha opinião é mais simples e fácil ... Pode haver muitas outras maneiras
Rohit Luthra
2

Ajuste rápido - para aqueles que estão tendo problemas com a configuração global da função transformRequest, aqui está o snippet que estou usando para se livrar do Cannot read property 'jquery' of undefinederro:

$httpProvider.defaults.transformRequest = function(data) {
        return data != undefined ? $.param(data) : null;
    }
kshep92
fonte
0

Achei muitas vezes um comportamento problemático desse todo. Usei-o do express (sem tipificações) e do bodyParser (com as tipificações dt ~ body-parser).

Não tentei fazer upload de um arquivo, em vez disso, simplesmente interpretar um JSON fornecido em uma string de postagem.

O request.bodyera simplesmente um json ( {}) vazio .

Depois de muita investigação, finalmente isso funcionou para mim:

import { json } from 'body-parser';
...
app.use(json()); <-- should be defined before the first POST handler!

Também pode ser importante fornecer o application/jsontipo de conteúdo na string de solicitação do lado do cliente.

peterh - Reintegrar Monica
fonte
Lamento pelo tipo de resposta "e sacrifique uma galinha preta", o que infelizmente é comum no estágio atual do ambiente de texto datilografado / nó / angular.
peterh - Reintegração de Monica em
0

Sintaxe para AngularJS v1.4.8 + (v1.5.0)

       $http.post(url, data, config)
            .then(
                    function (response) {
                        // success callback
                    },
                    function (response) {
                        // failure callback
                    }
            );

Por exemplo:

    var url = "http://example.com";

    var data = {
        "param1": "value1",
        "param2": "value2",
        "param3": "value3"
    };

    var config = {
        headers: {
            'Content-Type': "application/json"
        }
    };

    $http.post(url, data, config)
            .then(
                    function (response) {
                        // success callback
                    },
                    function (response) {
                        // failure callback
                    }
            );
Pranav VR
fonte