AngularJS: ¿alguna forma de que $ http.post envíe parámetros de solicitud en lugar de JSON?

116

Tengo un código antiguo que está haciendo una solicitud POST AJAX a través del método de publicación de jQuery y se parece a esto:

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

requestData es solo un objeto javascript con algunas propiedades básicas de cadena.

Estoy en el proceso de mover nuestras cosas para usar Angular, y quiero reemplazar esta llamada con $ http.post. Se me ocurrió lo siguiente:

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

Cuando hice esto, obtuve una respuesta de error 500 del servidor. Usando Firebug, descubrí que esto envió el cuerpo de la solicitud de esta manera:

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

El jQuery exitoso $.postenvía el cuerpo así:

param1=value1&param2=value2&param3=value3

El punto final al que estoy llegando espera parámetros de solicitud y no JSON. Entonces, mi pregunta es, ¿hay alguna forma de decirle $http.postque envíe el objeto javascript como parámetros de solicitud en lugar de JSON? Sí, sé que podría construir la cadena yo mismo a partir del objeto, pero quiero saber si Angular proporciona algo para esto fuera de la caja.

dnc253
fuente

Respuestas:

140

Creo que el paramsparámetro de configuración no funcionará aquí, ya que agrega la cadena a la URL en lugar del cuerpo, pero para agregar a lo que Infeligo sugirió aquí, hay un ejemplo de la anulación global de una transformación predeterminada (usando jQuery param como ejemplo para convertir los datos a la cadena de parámetros).

Configure la función transformRequest global:

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

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

De esa manera, todas las llamadas a $ http.post transformarán automáticamente el cuerpo al mismo formato de parámetro utilizado por la $.postllamada de jQuery .

Tenga en cuenta que también puede configurar el encabezado Content-Type por llamada o globalmente así:

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

Ejemplo transformRequest no global por llamada:

    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
fuente
Me preguntaba si había algo más que tener una función transformRequest, pero parece que no la hay. Gracias por recibir información sobre la función jQuery param.
dnc253
El método no global por llamada funciona bien para mí, pero cuando intento configurarlo globalmente a través de $httpProvider.defaults, entonces no funciona, ¿alguna pista sobre esto?
Dfr
1
WRT configurándolo globalmente, yo también estoy teniendo problemas. Cuando intento hacerlo con el fragmento que se proporciona aquí, aparece un error. Cannot read property "jquery" of undefined.¿Cómo puedo solucionar este problema? PD. Las transformaciones por llamada funcionan.
kshep92
@ kshep92 Lo que está sucediendo es que se llama a la función transformRequest en una solicitud sin datos, por lo que 'datos' no está definido. Agregué un guardia antes de 'return $ .param (data);'. Inserte esto como la primera línea de la función transformRequest: 'if (data === undefined) return data;' Vea la edición que hice a la respuesta.
Jere.Jones
1
a partir de Angular 1.4 puede usar $ httpParamSerializer en lugar de jQuery docs.angularjs.org/api/ng/service/$httpParamSerializer
theRemix
21

Si usa Angular> = 1.4 , aquí está la solución más limpia que he encontrado que no depende de nada personalizado o 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';
});

Y luego puedes hacer esto en cualquier lugar de tu aplicación:

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

Y serializará correctamente los datos param1=value1&param2=value2y los enviará /requesturlcon el application/x-www-form-urlencoded; charset=utf-8encabezado Content-Type como normalmente se espera con las solicitudes POST en los puntos finales.

Saeb Amini
fuente
17

De la documentación de AngularJS:

params - {Object.} - Mapa de cadenas u objetos que se convertirán en? key1 = value1 & key2 = value2 después de la URL. Si el valor no es una cadena , será JSONified.

Por lo tanto, proporcione cadenas como parámetros. Si no quieres eso, usa transformaciones. Nuevamente, de la documentación:

Para anular estas transformaciones localmente, especifique las funciones de transformación como propiedades transformRequest y / o transformResponse del objeto de configuración. Para anular globalmente las transformaciones predeterminadas, anule las propiedades $ httpProvider.defaults.transformRequest y $ httpProvider.defaults.transformResponse de $ httpProvider.

Consulte la documentación para obtener más detalles.

Infeligo
fuente
Vi los parámetros en la documentación y, como menciona Gloopy, lo necesito en el cuerpo y no en la URL. Me preguntaba si había alguna opción o algo que me faltaba para hacer los parámetros en lugar de JSON, pero parece que solo necesito usar la propiedad transformRequest.
dnc253
15

Utilice la $.paramfunción de jQuery para serializar los datos JSON en requestData.

En resumen, usando un código similar al suyo:

$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 esto, debe incluir jQuery en su página junto con AngularJS.

Sagar Bhosale
fuente
7

Tenga en cuenta que a partir de Angular 1.4, puede serializar los datos del formulario sin usar jQuery.

En app.js:

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

Luego en tu controlador:

$http({
    method: 'POST',
    url: myUrl',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    data: myData
});
Thomas Graziani
fuente
Esta respuesta es genial. Aborda los 2 problemas principales con Post from Angular. El encabezado debe estar configurado correctamente y debe serializar los datos json. Si no necesita compatibilidad con IE8, utilice 1.4+ o posterior.
mbokil
Acabo de implementar esto y resuelve los problemas que tenía con la publicación, pero esto también cambia la forma en que funciona el parche y parece haber roto todos mis usos de $ http.patch ().
Mike Feltman
5

Esto podría ser un truco, pero evité el problema y convertí el json en la matriz POST de PHP en el lado del servidor:

$_POST = json_decode(file_get_contents('php://input'), true);
TimoSolo
fuente
He usado este método, pero lo odio; y me tomó mucho tiempo descubrir por qué tenía que usar esto.
meconroy
como dije, se siente hacky. Como la mayoría de php;)
TimoSolo
5

También tengo problemas con la configuración de la autenticación http personalizada porque $ resource cache la solicitud.

Para que funcione, debe sobrescribir los encabezados existentes haciendo esto

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 haber podido ayudar a alguien. Me tomó 3 días resolver esto.

Frank Marcelo
fuente
Supongo que me acabas de ahorrar 3 días de trabajo. ¡¡¡Gracias!!! Todavía estoy tratando de averiguar si puedo interceptar la llamada de solicitud de alguna manera para poder inyectar un encabezado personalizado para cada llamada.
marcoseu
4

Modifique los encabezados predeterminados:

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

Luego usa el $.parammétodo de JQuery :

var payload = $.param({key: value});
$http.post(targetURL, payload);
Zags
fuente
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
fuente
Según yo, es más simple y fácil ... Puede haber muchas otras formas
Rohit Luthra
2

Ajuste rápido: para aquellos de ustedes que tengan problemas con la configuración global de la función transformRequest, aquí está el fragmento que estoy usando para deshacerme del Cannot read property 'jquery' of undefinederror:

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

Encontré muchas veces el comportamiento problemático de este conjunto. Lo usé de express (sin tipificaciones) y bodyParser (con las tipificaciones dt ~ body-parser).

No intenté cargar un archivo, sino simplemente interpretar un JSON dado en una cadena de publicación.

El request.bodyera simplemente un json ( {}) vacío .

Después de mucha investigación, finalmente esto funcionó para mí:

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

También puede ser importante proporcionar el application/jsontipo de contenido en la cadena de solicitud desde el lado del cliente.

peterh - Restablecer a Monica
fuente
Lamento la respuesta de estilo "y sacrificar una gallina negra", lo que desafortunadamente es común en la etapa actual del entorno mecanografiado / nodo / angular.
peterh - Reincorporar a Monica
0

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

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

P.ej:

    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
fuente