Wie kann ich Daten als Formulardaten anstelle einer Anforderungsnutzlast buchen?


523

Im folgenden Code $httpruft die AngularJS- Methode die URL auf und sendet das xsrf-Objekt als "Request Payload" (wie auf der Registerkarte "Chrome-Debugger-Netzwerk" beschrieben). Die jQuery- $.ajaxMethode führt denselben Aufruf aus, sendet jedoch xsrf als "Formulardaten".

Wie kann ich AngularJS veranlassen, xsrf als Formulardaten anstelle einer Anforderungsnutzlast zu senden?

var url = 'http://somewhere.com/';
var xsrf = {fkey: 'xsrf key'};

$http({
    method: 'POST',
    url: url,
    data: xsrf
}).success(function () {});

$.ajax({
    type: 'POST',
    url: url,
    data: xsrf,
    dataType: 'json',
    success: function() {}
});

1
Dies war eine sehr nützliche Frage. Es ermöglicht mir, eine Nutzlast als Zeichenfolge zu senden (durch Ändern des Inhaltstyps), wodurch verhindert wird, dass ich mich vor POST / GET mit OPTIONEN befassen muss.
EarthmeLon

Ich habe die gleiche Frage, nachdem ich die URL
angefordert habe

Antworten:


614

Die folgende Zeile muss dem übergebenen $ http-Objekt hinzugefügt werden:

headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}

Die übergebenen Daten sollten in eine URL-codierte Zeichenfolge konvertiert werden:

> $.param({fkey: "key"})
'fkey=key'

Sie haben also so etwas wie:

$http({
    method: 'POST',
    url: url,
    data: $.param({fkey: "key"}),
    headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
})

Von: https://groups.google.com/forum/#!msg/angular/5nAedJ1LyO0/4Vj_72EZcDsJ

AKTUALISIEREN

Informationen zum Verwenden neuer Dienste, die mit AngularJS V1.4 hinzugefügt wurden, finden Sie unter


3
Gibt es eine Möglichkeit, dass die json> url-Codierung der Daten automatisch erfolgt oder dass dies für jede POST- oder PUT-Methode angegeben wird?
Dogoku

51
+1 @mjibson, für mich hat es sogar nicht funktioniert, die Header zu übergeben, bis ich sah, dass Ihre Antwort Folgendes enthielt: Das ist var xsrf = $.param({fkey: "key"});dumm, warum kann Angular es nicht intern tun?
Naikus

12
Um dem Standardverhalten von $ .ajax näher zu kommen, sollte der Zeichensatz auch im Inhaltstyp-Header angegeben werden -headers: {Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
Imre

25
Anstatt die param-Funktion von jQuery zu verwenden, legen Sie einfach die params-Eigenschaft für die $ http-Anforderung fest und sie macht das, was die jQuery.param-Methode tut, solange der Content-Type-Header 'application / x-www-form-urlencoded' - stackoverflow lautet .com / Fragen / 18967307 /…
Spig

13
@spig Ja, es wird das tun, was jQuery.param tut, aber wenn Sie die Eigenschaft params verwenden, werden Ihre Eigenschaften als Teil der Anforderungs-URL anstatt im Hauptteil codiert - selbst wenn Sie die Anwendung / x-www- angegeben haben. formularkodierter Header.
Stian

194

Wenn Sie jQuery nicht in der Lösung verwenden möchten, können Sie dies versuchen. Lösung von hier geschnappt https://stackoverflow.com/a/1714899/1784301

$http({
    method: 'POST',
    url: url,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    transformRequest: function(obj) {
        var str = [];
        for(var p in obj)
        str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
        return str.join("&");
    },
    data: xsrf
}).success(function () {});

7
Diese Methode funktioniert für mich in Angular 1.2.x, und ich denke, sie ist die beste Antwort, da sie elegant ist, in Core Angular funktioniert und nicht von externen Bibliotheken wie jQuery abhängt.
Gregtczap

2
Bei der Verwendung dieser Methode innerhalb einer $ resource-Aktion ist ein Problem aufgetreten. Die Formulardaten enthielten auch Funktionen für $ get, $ save usw. Die Lösung bestand darin, die forAnweisung ein wenig zu ändern , um sie angular.forEachstattdessen zu verwenden .
Anthony

10
Beachten Sie, dass diese Methode im Gegensatz zu $ ​​.param () nicht rekursiv für Arrays / Objekte funktioniert.
MazeChaZer

1
Ich würde überprüfen, ob das obj[p]nicht null oder undefiniert ist . Andernfalls senden Sie am Ende "null" oder "undefiniert" als Wert.
Tamir

1
Ich habe nicht verstanden, transformRequest: function(obj)da obj undefiniert ist. Nehmen wir an, wir übergeben das xsrf? Gefällt transformRequest: function(xsrf)
mir

92

Die anhaltende Verwirrung um dieses Thema hat mich dazu inspiriert, einen Blog-Beitrag darüber zu schreiben. Die Lösung, die ich in diesem Beitrag vorschlage, ist besser als Ihre derzeit am besten bewertete Lösung, da Sie nicht darauf beschränkt sind, Ihr Datenobjekt für $ http-Dienstaufrufe zu parametrisieren. Mit meiner Lösung können Sie also einfach weiterhin tatsächliche Datenobjekte an $ http.post () usw. übergeben und trotzdem das gewünschte Ergebnis erzielen.

Die am besten bewertete Antwort basiert auch auf der Aufnahme von vollständigem jQuery in die Seite für die Funktion $ .param (), während meine Lösung jQuery-unabhängig und rein AngularJS-fähig ist.

http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/

Hoffe das hilft.


10
+1 für den detaillierten Blog, aber die Tatsache, dass es einen Bedarf dafür gibt, ist schrecklich ...
iwein

4
Ja, vielleicht schrecklich auf zwei Ebenen: 1) dass AngularJS beschlossen hat, einen De-facto- Standard (obwohl zugegebenermaßen fehlgeleitet) zu ändern , und 2) dass PHP (und wer auch immer andere serverseitige Sprachen kennt) Anwendung / JSON nicht automatisch erkennt Eingang. : P
Ezekiel Victor

Wäre es möglich, dass sich anglejs automatisch an den Inhaltstyp anpasst und entsprechend codiert? Ist es vorgesehen?
Unludo

4
Ich bin (wie viele andere auch) darauf gestoßen, dass mein Backend ASP.NETdies nicht "nativ" unterstützt hat. Wenn Sie das Verhalten von AngularJS nicht ändern möchten (was ich nicht getan habe, weil meine API JSON zurückgibt, warum sollte sie nicht auch JSON akzeptieren, es ist flexibler als Formulardaten), können Sie aus dem lesen Request.InputStreamund dann auf irgendeine Weise damit umgehen du willst es. (Ich habe es dynamicaus
Gründen der

2
Abgestimmt, weil dies keine eigenständige Antwort ist . Gute Antworten verlinken nicht nur woanders. Von wie man Antwort : „Immer zitiert den wichtigsten Teil einer wichtigen Verbindung, falls die Zielstelle nicht erreichbar ist oder permanent offline geht.“
James

83

Ich nahm einige der anderen Antworten und machte etwas sauberer. .config()Setzen Sie diesen Aufruf an das Ende Ihres angle.module in Ihrer app.js:

.config(['$httpProvider', function ($httpProvider) {
  // Intercept POST requests, convert to standard form encoding
  $httpProvider.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
  $httpProvider.defaults.transformRequest.unshift(function (data, headersGetter) {
    var key, result = [];

    if (typeof data === "string")
      return data;

    for (key in data) {
      if (data.hasOwnProperty(key))
        result.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
    }
    return result.join("&");
  });
}]);

1
Funktioniert wie ein Zauber - auch wenn er an eine Ressourcendefinition angehängt ist.
Kai Mattern

3
Auch darauf geachtet zu verwenden, unshift()damit die anderen Transformationen ungestört bleiben. Gute Arbeit.
Aditya MP

2
perfekt! hat gut funktioniert für mich! trauriger Winkel unterstützt dies nicht von Haus aus.
Spierala

2
Diese Antwort sollte die richtige oben sein, die anderen sind falsch, danke Kumpel !!
Jose Ignacio Hita

2
Wie wäre es mit rekursiver Codierung?
Petah

58

Ab AngularJS v1.4.0 gibt es einen integrierten $httpParamSerializerDienst, der jedes Objekt gemäß den auf der Dokumentseite aufgeführten Regeln in einen Teil einer HTTP-Anforderung konvertiert .

Es kann folgendermaßen verwendet werden:

$http.post('http://example.com', $httpParamSerializer(formDataObj)).
    success(function(data){/* response status 200-299 */}).
    error(function(data){/* response status 400-999 */});

Denken Sie daran, dass für einen korrekten Formularbeitrag die Content-TypeKopfzeile geändert werden muss. Um dies global für alle POST-Anfragen zu tun, kann dieser Code (aus Albireos halber Antwort entnommen) verwendet werden:

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

Um dies nur für den aktuellen Beitrag zu tun, muss die headersEigenschaft des Anforderungsobjekts geändert werden:

var req = {
 method: 'POST',
 url: 'http://example.com',
 headers: {
   'Content-Type': 'application/x-www-form-urlencoded'
 },
 data: $httpParamSerializer(formDataObj)
};

$http(req);

Wie können wir dasselbe in einer benutzerdefinierten $ resource-Fabrik tun?
Stillleben

Hinweis: Ich aktualisiere eine App von Angular 1.3 auf 1.5. Es hat die Header in transformRequest geändert. Aus irgendeinem Grund funktioniert die oben beschriebene Methode bei mir nicht. Angular fügt doppelte Anführungszeichen um die URL-codierte Zeichenfolge hinzu. Gelöst mit transformRequest: $httpParamSerializer, data: formDataObj. Danke für die Lösung.
PhiLho

24

Sie können das Verhalten global definieren:

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

Sie müssen es also nicht jedes Mal neu definieren:

$http.post("/handle/post", {
    foo: "FOO",
    bar: "BAR"
}).success(function (data, status, headers, config) {
    // TODO
}).error(function (data, status, headers, config) {
    // TODO
});

46
Ihr Beispiel ist so falsch ... Alles, was Sie ändern, ist der Header. Die Daten selbst sind weiterhin JSON-codiert und für ältere Server, die JSON nicht lesen können, nicht lesbar.
Alexk

victorblog.com/2012/12/20/… - Hier ist ein gutes Beispiel, in dem Sie den Standard-Header $ http überschreiben und das Objekt in serialisierte Formulardaten konvertieren.
Federico

20

Um dieses Problem zu umgehen, können Sie den Code, der den POST empfängt, einfach auf Anwendungs- / JSON-Daten reagieren lassen. Für PHP habe ich den folgenden Code hinzugefügt, damit ich ihn entweder formularcodiert oder JSON POSTEN kann.

//handles JSON posted arguments and stuffs them into $_POST
//angular's $http makes JSON posts (not normal "form encoded")
$content_type_args = explode(';', $_SERVER['CONTENT_TYPE']); //parse content_type string
if ($content_type_args[0] == 'application/json')
  $_POST = json_decode(file_get_contents('php://input'),true);

//now continue to reference $_POST vars as usual

Dies ist eines der guten Beispiele für serverseitige Korrekturen, da das eigentliche Problem bei diesem Problem in der serverseitigen API liegt. bravo
Vignesh

16

Diese Antworten sehen aus wie verrückter Overkill, manchmal ist einfach einfach besser:

$http.post(loginUrl, "userName=" + encodeURIComponent(email) +
                     "&password=" + encodeURIComponent(password) +
                     "&grant_type=password"
).success(function (data) {
//...

1
Für mich musste ich noch den Header angeben Content-Typeund auf setzen application/x-www-form-urlencoded.
Victor Ramos

9

Sie können mit der folgenden Lösung versuchen

$http({
        method: 'POST',
        url: url-post,
        data: data-post-object-json,
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        transformRequest: function(obj) {
            var str = [];
            for (var key in obj) {
                if (obj[key] instanceof Array) {
                    for(var idx in obj[key]){
                        var subObj = obj[key][idx];
                        for(var subKey in subObj){
                            str.push(encodeURIComponent(key) + "[" + idx + "][" + encodeURIComponent(subKey) + "]=" + encodeURIComponent(subObj[subKey]));
                        }
                    }
                }
                else {
                    str.push(encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]));
                }
            }
            return str.join("&");
        }
    }).success(function(response) {
          /* Do something */
        });

8

Erstellen Sie einen Adapterdienst für die Post:

services.service('Http', function ($http) {

    var self = this

    this.post = function (url, data) {
        return $http({
            method: 'POST',
            url: url,
            data: $.param(data),
            headers: {'Content-Type': 'application/x-www-form-urlencoded'}
        })
    }

}) 

Verwenden Sie es in Ihren Controllern oder was auch immer:

ctrls.controller('PersonCtrl', function (Http /* our service */) {
    var self = this
    self.user = {name: "Ozgur", eMail: null}

    self.register = function () {
        Http.post('/user/register', self.user).then(function (r) {
            //response
            console.log(r)
        })
    }

})

$ .param nur in jquery abi. jsfiddle.net/4n9fao9q/27 $ httpParamSerializer ist Angularjs Äquivalent.
Dexter

7

Es gibt ein wirklich nettes Tutorial, das dieses und andere verwandte Themen behandelt - Einreichen von AJAX-Formularen: The AngularJS Way .

Grundsätzlich müssen Sie den Header der POST-Anforderung festlegen, um anzuzeigen, dass Sie Formulardaten als URL-codierte Zeichenfolge senden, und die zu sendenden Daten im selben Format festlegen

$http({
  method  : 'POST',
  url     : 'url',
  data    : $.param(xsrf),  // pass in data as strings
  headers : { 'Content-Type': 'application/x-www-form-urlencoded' }  // set the headers so angular passing info as form data (not request payload)
});

Beachten Sie, dass die Hilfsfunktion param () von jQuery hier zum Serialisieren der Daten in eine Zeichenfolge verwendet wird. Sie können dies jedoch auch manuell tun, wenn Sie jQuery nicht verwenden.


1
Die Moderatoren haben meine vorherige Antwort einfach gelöscht, da ich keine Details zur tatsächlichen Implementierung angegeben habe, die im Link erwähnt wird. Es wäre besser gewesen, wenn sie mich stattdessen zuerst gebeten hätten, weitere Details anzugeben, anstatt sie zu löschen, da ich meine Antwort bereits bearbeitet habe, um die in dieser Antwort gezeigten Details anzugeben!
Robinmitra

Die $.parammachen die Magie. perfekte Lösung für diejenigen, die eine auf jQuery + AngularJS basierende App haben.
Dashtinejad


4

Für Symfony2-Benutzer:

Wenn Sie nichts in Ihrem Javascript ändern möchten, damit dies funktioniert, können Sie diese Änderungen in Ihrer Symfony-App vornehmen:

Erstellen Sie eine Klasse, die die Klasse Symfony \ Component \ HttpFoundation \ Request erweitert:

<?php

namespace Acme\Test\MyRequest;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ParameterBag;

class MyRequest extends Request{


/**
* Override and extend the createFromGlobals function.
* 
* 
*
* @return Request A new request
*
* @api
*/
public static function createFromGlobals()
{
  // Get what we would get from the parent
  $request = parent::createFromGlobals();

  // Add the handling for 'application/json' content type.
  if(0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/json')){

    // The json is in the content
    $cont = $request->getContent();

    $json = json_decode($cont);

    // ParameterBag must be an Array.
    if(is_object($json)) {
      $json = (array) $json;
  }
  $request->request = new ParameterBag($json);

}

return $request;

}

}

Verwenden Sie jetzt Ihre Klasse in app_dev.php (oder einer beliebigen Indexdatei, die Sie verwenden).

// web/app_dev.php

$kernel = new AppKernel('dev', true);
// $kernel->loadClassCache();
$request = ForumBundleRequest::createFromGlobals();

// use your class instead
// $request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

Das war wirklich nützlich für mich, die neuen createFromGlobals funktionieren jetzt perfekt. Ich weiß nicht, warum Sie eine Ablehnung erhalten haben, aber ich habe sie entfernt.
Richard

3

Nur den Inhaltstyp festzulegen ist nicht genug, URL-Codierung von Formulardaten vor dem Senden. $http.post(url, jQuery.param(data))


3

Ich verwende derzeit die folgende Lösung , die ich gefunden in der AngularJS Gruppe googeln.

$ http
.post ('/ echo / json /', 'json =' + encodeURIComponent (angle.toJson (data)), {
    Überschriften: {
        'Inhaltstyp': 'application / x-www-form-urlencoded; Zeichensatz = UTF-8 '
    }}
}). Erfolg (Funktion (Daten) {
    $ scope.data = data;
});

Beachten Sie, dass Sie, wenn Sie PHP verwenden, etwas wie Symfony 2-HTTP-Komponenten verwenden müssen Request::createFromGlobals(), um dies zu lesen, da $ _POST nicht automatisch damit geladen wird.


2

AngularJS macht es richtig, da es den folgenden Inhaltstyp im http-Request-Header ausführt:

Content-Type: application/json

Wenn Sie mit PHP wie mir oder sogar mit Symfony2 arbeiten, können Sie einfach Ihre Serverkompatibilität für den json-Standard wie hier beschrieben erweitern: http://silex.sensiolabs.org/doc/cookbook/json_request_body.html

Der Symfony2-Weg (z. B. in Ihrem DefaultController):

$request = $this->getRequest();
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
    $data = json_decode($request->getContent(), true);
    $request->request->replace(is_array($data) ? $data : array());
}
var_dump($request->request->all());

Der Vorteil wäre, dass Sie den jQuery-Parameter nicht verwenden müssen und AngularJS als native Methode zur Ausführung solcher Anforderungen verwenden können.


2

Vollständige Antwort (seit Winkel 1.4). Sie müssen de dependency $ httpParamSerializer einschließen

var res = $resource(serverUrl + 'Token', { }, {
                save: { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
            });

            res.save({ }, $httpParamSerializer({ param1: 'sdsd', param2: 'sdsd' }), function (response) {

            }, function (error) { 

            });

1

In Ihrer App-Konfiguration -

$httpProvider.defaults.transformRequest = function (data) {
        if (data === undefined)
            return data;
        var clonedData = $.extend(true, {}, data);
        for (var property in clonedData)
            if (property.substr(0, 1) == '$')
                delete clonedData[property];

        return $.param(clonedData);
    };

Mit Ihrer Ressourcenanfrage -

 headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            }

0

Dies ist keine direkte Antwort, sondern eine etwas andere Designrichtung:

Veröffentlichen Sie die Daten nicht als Formular, sondern als JSON-Objekt, das direkt dem serverseitigen Objekt zugeordnet werden soll, oder verwenden Sie die Pfadvariable im REST-Stil

Jetzt weiß ich, dass keine der beiden Optionen in Ihrem Fall geeignet ist, da Sie versuchen, einen XSRF-Schlüssel zu übergeben. Die Zuordnung zu einer Pfadvariablen wie dieser ist ein schreckliches Design:

http://www.someexample.com/xsrf/{xsrfKey}

Denn von Natur aus wollen Sie auch XSRF Schlüssel zum anderen Weg passieren, /login, /book-appointmentusw. , und Sie wollen nicht zu verwirren Ihre hübsche URL

Interessanterweise ist das Hinzufügen als Objektfeld ebenfalls nicht geeignet, da Sie jetzt für jedes JSON-Objekt, das Sie an den Server übergeben, das Feld hinzufügen müssen

{
  appointmentId : 23,
  name : 'Joe Citizen',
  xsrf : '...'
}

Sie möchten Ihrer serverseitigen Klasse sicherlich kein weiteres Feld hinzufügen, das keine direkte semantische Zuordnung zum Domänenobjekt hat.

Meiner Meinung nach ist der beste Weg, Ihren xsrf-Schlüssel zu übergeben, ein HTTP-Header. Viele serverseitige Webframework-Bibliotheken mit xsrf-Schutz unterstützen dies. In Java Spring können Sie es beispielsweise über den X-CSRF-TOKENHeader übergeben .

Die hervorragende Fähigkeit von Angular, JS-Objekte an UI-Objekte zu binden, bedeutet, dass wir die Praxis des gemeinsamen Postens von Formularen loswerden und stattdessen JSON veröffentlichen können. JSON kann leicht in serverseitige Objekte de-serialisiert werden und unterstützt komplexe Datenstrukturen wie Map, Arrays, verschachtelte Objekte usw.

Wie postet man ein Array in einer Formularnutzlast? Vielleicht so:

shopLocation=downtown&daysOpen=Monday&daysOpen=Tuesday&daysOpen=Wednesday

oder dieses:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday

Beide sind schlechtes Design ..


0

Dies ist, was ich für meine Bedürfnisse tue, wo ich die Anmeldedaten als Formulardaten an die API senden muss und das Javascript-Objekt (userData) automatisch in URL-codierte Daten konvertiert wird

        var deferred = $q.defer();
        $http({
            method: 'POST',
            url: apiserver + '/authenticate',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            transformRequest: function (obj) {
                var str = [];
                for (var p in obj)
                    str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
                return str.join("&");
            },
            data: userData
        }).success(function (response) {
            //logics
            deferred.resolve(response);
        }).error(function (err, status) {
           deferred.reject(err);
        });

So sind meine Userdata

var userData = {
                grant_type: 'password',
                username: loginData.userName,
                password: loginData.password
            }

-1

Das einzige Problem, das Sie ändern müssen, ist die Verwendung der Eigenschaft "params" anstelle von "data", wenn Sie Ihr $ http-Objekt erstellen:

$http({
   method: 'POST',
   url: serviceUrl + '/ClientUpdate',
   params: { LangUserId: userId, clientJSON: clients[i] },
})

Im obigen Beispiel ist clients [i] nur ein JSON-Objekt (in keiner Weise serialisiert). Wenn Sie "params" anstelle von "data" verwenden, serialisiert Angular das Objekt für Sie mithilfe von $ httpParamSerializer: https://docs.angularjs.org/api/ng/service/ $ httpParamSerializer


2
Durch die Verwendung von Parametern anstelle von Daten platziert Angular die Daten in den URL-Parametern anstelle des Anforderungshauptteils. Dies ist nicht das, was von einem Formularbeitrag erwartet wird.
Cmenning

-3

Verwenden Sie den AngularJS- $httpDienst und verwenden Sie dessen postMethode oder Konfigurationsfunktion $http.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.