AngularJS - Gibt es eine Möglichkeit für $ http.post, Anforderungsparameter anstelle von JSON zu senden?

116

Ich habe einen alten Code, der eine AJAX-POST-Anfrage über die Post-Methode von jQuery stellt und ungefähr so aussieht:

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

requestData ist nur ein Javascript-Objekt mit einigen grundlegenden Zeichenfolgeneigenschaften.

Ich bin gerade dabei, unsere Daten auf Angular umzustellen, und ich möchte diesen Aufruf durch $ http.post ersetzen. Ich habe mir Folgendes ausgedacht:

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

Als ich dies tat, erhielt ich eine 500-Fehler-Antwort vom Server. Bei Verwendung von Firebug stellte ich fest, dass dies den Anfragetext wie folgt gesendet hat:

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

Die erfolgreiche jQuery $.postsendet den Text folgendermaßen:

param1=value1&param2=value2&param3=value3

Der Endpunkt, den ich treffe, erwartet Anforderungsparameter und nicht JSON. Meine Frage ist also, ob es überhaupt zu sagen gibt, dass $http.postdas Javascript-Objekt als Anforderungsparameter anstelle von JSON gesendet werden soll. Ja, ich weiß, dass ich die Zeichenfolge selbst aus dem Objekt erstellen kann, aber ich möchte wissen, ob Angular sofort etwas dafür bereitstellt.

dnc253
quelle

Antworten:

140

Ich denke, der paramsKonfigurationsparameter funktioniert hier nicht, da er die Zeichenfolge anstelle des Textes zur URL hinzufügt. Zu dem, was Infeligo hier vorschlägt, gehört jedoch ein Beispiel für die globale Überschreibung einer Standardtransformation (wobei der Konvertierungsparameter jQuery als Beispiel verwendet wird die Daten zu param string).

Richten Sie die globale transformRequest-Funktion ein:

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

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

Auf diese Weise transformieren alle Aufrufe von $ http.post den Body automatisch in dasselbe Parameterformat, das vom jQuery- $.postAufruf verwendet wird.

Beachten Sie, dass Sie den Content-Type-Header möglicherweise auch pro Aufruf oder global wie folgt festlegen möchten:

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

Beispiel für eine nicht globale transformRequest pro Aufruf:

    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
quelle
Ich habe mich gefragt, ob es etwas anderes als eine transformRequest-Funktion gibt, aber es hört sich so an, als gäbe es keine. Vielen Dank für die Hinweise zur jQuery-Parameterfunktion.
dnc253
Die nicht globale Methode pro Anruf funktioniert gut für mich, aber wenn Sie versuchen, global über einzurichten $httpProvider.defaults, funktioniert sie dann nicht. Gibt es einen Hinweis darauf?
Dfr
1
WRT konfiguriert es global, ich habe auch Probleme. Wenn ich versuche, dies mit dem hier angegebenen Snippet zu tun, wird eine Fehlermeldung angezeigt. Cannot read property "jquery" of undefined.Wie behebe ich das? PS. Transformationen pro Anruf funktionieren.
kshep92
@ kshep92 Was passiert ist, dass die transformRequest-Funktion bei einer Anforderung ohne Daten aufgerufen wird, sodass 'data' undefiniert ist. Ich habe vor 'return $ .param (data);' einen Guard hinzugefügt. Fügen Sie dies als erste Zeile in die transformRequest-Funktion ein: 'if (data === undefined) return data;' Siehe die Bearbeitung, die ich an der Antwort vorgenommen habe.
Jere.Jones
1
Ab Angular 1.4 können Sie $ httpParamSerializer anstelle von jQuery docs.angularjs.org/api/ng/service/$httpParamSerializer
theRemix
21

Wenn Sie Angular> = 1.4 verwenden , ist hier die sauberste Lösung, die ich gefunden habe und die sich nicht auf benutzerdefinierte oder externe Elemente stützt:

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

Und dann können Sie dies überall in Ihrer App tun:

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

Außerdem werden die Daten korrekt als serialisiert param1=value1&param2=value2und /requesturlmit dem application/x-www-form-urlencoded; charset=utf-8Content-Type-Header an gesendet, wie dies normalerweise bei POST-Anforderungen auf Endpunkten zu erwarten ist.

Saeb Amini
quelle
17

Aus der AngularJS-Dokumentation:

params - {Object.} - Zuordnung von Zeichenfolgen oder Objekten, die nach der URL in? key1 = value1 & key2 = value2 umgewandelt werden. Wenn der Wert keine Zeichenfolge ist , wird er JSONified.

Geben Sie also einen String als Parameter an. Wenn Sie das nicht wollen, verwenden Sie Transformationen. Nochmals aus der Dokumentation:

Um diese Transformation lokal zu überschreiben, geben Sie Transformationsfunktionen als transformRequest- und / oder transformResponse-Eigenschaften des Konfigurationsobjekts an. Um die Standardtransformationen global zu überschreiben, überschreiben Sie die Eigenschaften $ httpProvider.defaults.transformRequest und $ httpProvider.defaults.transformResponse des $ httpProvider.

Weitere Informationen finden Sie in der Dokumentation .

Infeligo
quelle
Ich habe die Parameter in der Dokumentation gesehen und wie Gloopy erwähnt, brauche ich sie im Text und nicht in der URL. Ich habe mich gefragt, ob es eine Option oder etwas gibt, das mir fehlt, um die Parameter anstelle von JSON auszuführen, aber es hört sich so an, als müsste ich nur die transformRequest-Eigenschaft verwenden.
dnc253
15

Verwenden Sie die $.paramFunktion von jQuery, um die JSON-Daten in requestData zu serialisieren.

Kurz gesagt, verwenden Sie einen ähnlichen Code wie Ihren:

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

Um dies zu verwenden, müssen Sie jQuery zusammen mit AngularJS in Ihre Seite aufnehmen.

Sagar Bhosale
quelle
7

Beachten Sie, dass Sie ab Angular 1.4 die Formulardaten ohne Verwendung von jQuery serialisieren können.

In der app.js:

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

Dann in Ihrem Controller:

$http({
    method: 'POST',
    url: myUrl',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    data: myData
});
Thomas Graziani
quelle
Diese Antwort ist großartig. Es behandelt die 2 Hauptprobleme mit Post from Angular. Der Header muss korrekt gesetzt sein und Sie müssen die JSON-Daten serialisieren. Wenn Sie keine IE8-Unterstützung benötigen, verwenden Sie 1.4+ oder höher.
mbokil
Ich habe dies gerade implementiert und es behebt Probleme, die ich mit dem Post hatte, aber dies ändert auch die Funktionsweise des Patches und scheint alle meine Verwendungen von $ http.patch () gebrochen zu haben.
Mike Feltman
5

Dies mag ein bisschen hacken, aber ich habe das Problem vermieden und den JSON auf der Serverseite in das POST-Array von PHP konvertiert:

$_POST = json_decode(file_get_contents('php://input'), true);
TimoSolo
quelle
Ich habe diese Methode angewendet, aber ich hasse sie. und ich habe lange gebraucht, um herauszufinden, warum ich das benutzen musste.
Mekonroy
wie ich schon sagte - es fühlt sich hacky an. Wie die meisten von PHP;)
TimoSolo
5

Ich habe auch Probleme beim Festlegen der benutzerdefinierten http-Authentifizierung, da $ resource die Anforderung zwischenspeichert.

Damit es funktioniert, müssen Sie die vorhandenen Header auf diese Weise überschreiben

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

Ich hoffe ich konnte jemandem helfen. Ich habe 3 Tage gebraucht, um das herauszufinden.

Frank Marcelo
quelle
Ich denke, Sie haben mir gerade 3 Arbeitstage erspart. Vielen Dank!!! Ich versuche immer noch herauszufinden, ob ich einen Anforderungsanruf irgendwie abfangen kann, damit ich für jeden Anruf einen benutzerdefinierten Header einfügen kann.
Marcoseu
4

Ändern Sie die Standardheader:

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

Verwenden Sie dann die $.paramMethode von JQuery :

var payload = $.param({key: value});
$http.post(targetURL, payload);
Zags
quelle
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
quelle
Meiner Meinung nach ist es am einfachsten und einfachsten ... Es kann viele andere Möglichkeiten geben
Rohit Luthra
2

Schnelle Anpassung - Für diejenigen unter Ihnen, die Probleme mit der globalen Konfiguration der transformRequest-Funktion haben, ist hier das Snippet, das ich verwende, um den Cannot read property 'jquery' of undefinedFehler zu beseitigen:

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

Ich fand oft problematisches Verhalten dieses Ganzen. Ich habe es von Express (ohne Typisierung) und BodyParser (mit den Typisierungen dt ~ body-parser) verwendet.

Ich habe nicht versucht, eine Datei hochzuladen, sondern einfach einen JSON zu interpretieren, der in einer Post-Zeichenfolge angegeben ist.

Das request.bodywar einfach ein leerer json ( {}).

Nach vielen Nachforschungen hat das bei mir endlich geklappt:

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

Es kann auch wichtig sein, den application/jsonInhaltstyp in der Anforderungszeichenfolge von der Clientseite anzugeben.

Peter - Setzen Sie Monica wieder ein
quelle
Es tut mir leid für die Antwort im Stil "und opfere eine schwarze Henne", die in der aktuellen Phase der Typoskript- / Knoten- / Winkelumgebung leider üblich ist.
Peterh - Wiedereinsetzung Monica
0

Syntax für AngularJS v1.4.8 + (v1.5.0)

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

Z.B:

    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
quelle