AngularJS - Un moyen pour $ http.post d'envoyer des paramètres de requête au lieu de JSON?

116

J'ai un vieux code qui fait une requête AJAX POST via la méthode post de jQuery et ressemble à ceci:

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

requestData est juste un objet javascript avec quelques propriétés de chaîne de base.

Je suis en train de déplacer nos trucs pour utiliser Angular, et je veux remplacer cet appel par $ http.post. J'ai trouvé ce qui suit:

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

Quand j'ai fait cela, j'ai reçu une réponse d'erreur 500 du serveur. En utilisant Firebug, j'ai trouvé que cela envoyait le corps de la requête comme ceci:

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

Le jQuery réussi $.postenvoie le corps comme ceci:

param1=value1&param2=value2&param3=value3

Le point de terminaison que je frappe attend des paramètres de demande et non JSON. Donc, ma question est: y a-t-il de toute façon à dire $http.postd'envoyer l'objet javascript en tant que paramètres de requête au lieu de JSON? Oui, je sais que je pourrais construire la chaîne moi-même à partir de l'objet, mais je veux savoir si Angular fournit quelque chose pour cela hors de la boîte.

dnc253
la source

Réponses:

140

Je pense que le paramsparamètre de configuration ne fonctionnera pas ici car il ajoute la chaîne à l'url au lieu du corps, mais pour ajouter à ce que Infeligo a suggéré, voici un exemple de remplacement global d'une transformation par défaut (en utilisant le paramètre jQuery comme exemple pour convertir les données en chaîne de paramètres).

Configurez la fonction globale transformRequest:

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

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

De cette façon, tous les appels à $ http.post transformeront automatiquement le corps au même format de paramètre utilisé par l' $.postappel jQuery .

Notez que vous pouvez également définir l'en-tête Content-Type par appel ou globalement comme ceci:

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

Exemple de transformRequest non global par appel:

    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
la source
Je me demandais s'il y avait autre chose que d'avoir une fonction transformRequest, mais il semble que ce n'est pas le cas. Merci de nous avoir informés de la fonction jQuery param.
dnc253
La méthode non globale par appel fonctionne bien pour moi, mais lorsque vous essayez de configurer globalement via $httpProvider.defaults, cela ne fonctionne pas, un indice à ce sujet?
Dfr
1
WRT le configurant globalement, j'ai aussi des problèmes. Lorsque j'essaie de le faire à l'aide de l'extrait de code donné ici, j'obtiens une erreur Cannot read property "jquery" of undefined.Comment résoudre ce problème? PS. Les transformations par appel fonctionnent.
kshep92
@ kshep92 Ce qui se passe, c'est que la fonction transformRequest est appelée sur une requête sans données, donc «data» n'est pas défini. J'ai ajouté une garde avant de 'return $ .param (data);'. Insérez ceci comme première ligne de la fonction transformRequest: 'if (data === undefined) return data;' Voir la modification que j'ai apportée à la réponse.
Jere.Jones
1
à partir d'Angular 1.4, vous pouvez utiliser $ httpParamSerializer au lieu de jQuery docs.angularjs.org/api/ng/service/$httpParamSerializer
theRemix
21

Si vous utilisez Angular> = 1.4 , voici la solution la plus propre que j'ai trouvée qui ne repose sur rien de personnalisé ou d'externe:

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

Et puis vous pouvez le faire n'importe où dans votre application:

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

Et il sérialisera correctement les données en tant que param1=value1&param2=value2et les enverra /requesturlavec l'en application/x-www-form-urlencoded; charset=utf-8-tête Content-Type comme cela est normalement prévu avec les requêtes POST sur les points de terminaison.

Saeb Amini
la source
17

De la documentation AngularJS:

params - {Object.} - Carte des chaînes ou objets qui seront transformés en? key1 = value1 & key2 = value2 après l'url. Si la valeur n'est pas une chaîne , elle sera JSONified.

Donc, fournissez une chaîne comme paramètres. Si vous ne le souhaitez pas, utilisez des transformations. Encore une fois, d'après la documentation:

Pour remplacer ces transformations localement, spécifiez les fonctions de transformation en tant que propriétés transformRequest et / ou transformResponse de l'objet de configuration. Pour remplacer globalement les transformations par défaut, remplacez les propriétés $ httpProvider.defaults.transformRequest et $ httpProvider.defaults.transformResponse de $ httpProvider.

Reportez-vous à la documentation pour plus de détails.

Inféligo
la source
J'ai vu les paramètres dans la documentation, et comme Gloopy le mentionne, j'en ai besoin dans le corps, et non sur l'URL. Je me demandais s'il y avait une option ou quelque chose qui me manquait pour faire les paramètres au lieu de JSON, mais il semble que je doive simplement utiliser la propriété transformRequest.
dnc253
15

Utilisez la $.paramfonction de jQuery pour sérialiser les données JSON dans requestData.

En bref, en utilisant un code similaire au vôtre:

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

Pour l'utiliser, vous devez inclure jQuery dans votre page avec AngularJS.

Sagar Bhosale
la source
7

Notez qu'à partir d'Angular 1.4, vous pouvez sérialiser les données du formulaire sans utiliser jQuery.

Dans l'app.js:

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

Puis dans votre contrôleur:

$http({
    method: 'POST',
    url: myUrl',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    data: myData
});
Thomas Graziani
la source
Cette réponse est excellente. Il aborde les 2 principaux problèmes avec Post d'Angular. L'en-tête doit être défini correctement et vous devez sérialiser les données json. Si vous n'avez pas besoin du support IE8, utilisez la version 1.4+ ou une version ultérieure.
mbokil le
Je viens de l'implémenter et cela résout les problèmes que j'avais avec la publication, mais cela change également le fonctionnement du patch et semble avoir interrompu toutes mes utilisations de $ http.patch ().
Mike Feltman
5

Cela peut être un peu un hack, mais j'ai évité le problème et converti le json en tableau POST de PHP côté serveur:

$_POST = json_decode(file_get_contents('php://input'), true);
TimoSolo
la source
J'ai utilisé cette méthode, mais je la déteste; et il m'a fallu beaucoup de temps pour comprendre pourquoi je devais l'utiliser.
meconroy
comme je l'ai dit - il se sent hacky. Comme la plupart des php;)
TimoSolo
5

J'ai également des problèmes avec la configuration de l'authentification http personnalisée car $ resource cache la demande.

Pour que cela fonctionne, vous devez écraser les en-têtes existants en faisant ceci

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'}
      },
    }
);

J'espère avoir pu aider quelqu'un. Il m'a fallu 3 jours pour comprendre celui-ci.

Frank Marcelo
la source
Je suppose que vous venez de m'économiser 3 jours de travail. Merci!!! J'essaie toujours de déterminer si je peux intercepter l'appel de demande d'une manière ou d'une autre afin de pouvoir injecter un en-tête personnalisé pour chaque appel.
marcoseu
4

Modifiez les en-têtes par défaut:

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

Ensuite, utilisez la $.paramméthode de JQuery :

var payload = $.param({key: value});
$http.post(targetURL, payload);
Zags
la source
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
la source
Selon moi, c'est le plus simple et le plus facile ... Il peut y avoir de nombreuses autres façons
Rohit Luthra
2

Réglage rapide - pour ceux d'entre vous qui ont des problèmes avec la configuration globale de la fonction transformRequest, voici l'extrait que j'utilise pour me débarrasser de l' Cannot read property 'jquery' of undefinederreur:

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

J'ai trouvé plusieurs fois le comportement problématique de cet ensemble. Je l'ai utilisé à partir d'express (sans typages) et du bodyParser (avec les typages dt ~ body-parser).

Je n'ai pas essayé de télécharger un fichier, mais simplement d'interpréter un JSON donné dans une chaîne de publication.

Le request.bodyétait tout simplement un vide JSON ( {}).

Après de nombreuses recherches, cela a finalement fonctionné pour moi:

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

Il peut également être important de donner le application/jsontype de contenu dans la chaîne de requête du côté client.

peterh - Réintégrer Monica
la source
Je suis désolé pour la réponse de type «et sacrifier une poule noire», ce qui est malheureusement courant dans la phase actuelle de l'environnement dactylographié / nœud / angulaire.
peterh - Réintégrer Monica le
0

Syntaxe pour AngularJS v1.4.8 + (v1.5.0)

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

Par exemple:

    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
la source