web-dev-qa-db-fra.com

Comment puis-je publier des données en tant que données de formulaire au lieu d'une charge de requête?

Dans le code ci-dessous, la méthode AngularJS $http appelle l'adresse URL et soumet l'objet xsrf en tant que "Demande de charge utile" (comme décrit dans l'onglet réseau Chrome du débogueur). La méthode jQuery $.ajax effectue le même appel mais soumet xsrf en tant que "données de formulaire".

Comment puis-je obliger AngularJS à soumettre xsrf en tant que données de formulaire au lieu d’une charge de requête?

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() {}
});
513
mjibson

La ligne suivante doit être ajoutée à l'objet $ http transmis:

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

Et les données transmises doivent être converties en une chaîne encodée en URL:

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

Donc vous avez quelque chose comme:

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

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

MISE À JOUR

Pour utiliser les nouveaux services ajoutés avec AngularJS V1.4, voir

604
mjibson

Si vous ne voulez pas utiliser jQuery dans la solution, vous pouvez essayer ceci. Solution nabbed d'ici 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 () {});
192
Anthony

La confusion qui règne autour de ce problème m'a incité à écrire un billet de blog à ce sujet. La solution que je propose dans cet article est meilleure que votre solution actuelle la mieux cotée, car elle ne vous oblige pas à paramétrer votre objet de données pour les appels de service $ http; c'est-à-dire qu'avec ma solution, vous pouvez simplement continuer à transmettre des objets de données réels à $ http.post (), etc. tout en obtenant le résultat souhaité.

De plus, la réponse la mieux notée repose sur l’inclusion de jQuery complet dans la page pour la fonction $ .param (), alors que ma solution est jnu agnostique, pure AngularJS ready.

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

J'espère que cela t'aides.

91
Ezekiel Victor

J'ai pris quelques-unes des autres réponses et fait quelque chose d'un peu plus propre, mettez cet appel .config() à la fin de votre angular.module dans votre 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("&");
  });
}]);
82
kzar

À partir de AngularJS v1.4.0, il existe un service intégré $httpParamSerializer qui convertit tout objet en partie d'une demande HTTP conformément aux règles répertoriées dans page docs .

Il peut être utilisé comme ceci:

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

N'oubliez pas que pour une publication de formulaire correcte, l'en-tête Content-Type doit être modifié. Pour faire ceci globalement pour toutes les demandes POST, ce code (tiré de la demi-réponse d'Albireo) peut être utilisé:

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

Pour faire cela uniquement pour la publication en cours, la propriété headers de l'objet request doit être modifiée:

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

$http(req);
57
Mitja

Vous pouvez définir le comportement globalement:

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

Donc, vous n'avez pas à le redéfinir à chaque fois:

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

En guise de solution de contournement, vous pouvez simplement faire en sorte que le code recevant le POST réponde aux données application/json. Pour PHP, j'ai ajouté le code ci-dessous, ce qui me permet de POST _ sous forme de formulaire ou JSON.

//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
20
James Bell

Ces réponses ressemblent à une surdose folle, parfois, simple est tout simplement meilleur:

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

Vous pouvez essayer avec la solution ci-dessous

$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 */
        });
9
tmquang6805

Créez un service d'adaptateur pour 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'}
        })
    }

}) 

Utilisez-le dans vos contrôleurs ou peu importe:

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

})
8
Ozgur

Il y a un très bon tutoriel qui traite de cela et d'autres choses connexes - Envoi de AJAX Formulaires: à la manière AngularJS .

En gros, vous devez définir l'en-tête de la demande POST pour indiquer que vous envoyez des données de formulaire sous forme de chaîne encodée en URL et définir le même format pour l'envoi des données.

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

Notez que la fonction d'assistance param () de jQuery est utilisée ici pour sérialiser les données dans une chaîne, mais vous pouvez également le faire manuellement si vous n'utilisez pas jQuery.

7
robinmitra
var fd = new FormData();
    fd.append('file', file);
    $http.post(uploadUrl, fd, {
        transformRequest: angular.identity,
        headers: {'Content-Type': undefined}
    })
    .success(function(){
    })
    .error(function(){
    });

Veuillez vérifier s'il vous plait! https://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs

7
aul

Pour les utilisateurs Symfony2:

Si vous ne voulez rien changer à votre javascript pour que cela fonctionne, vous pouvez faire ces modifications dans votre application symfony:

Créez une classe qui étend la classe Symfony\Component\HttpFoundation\Request:

<?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;

}

}

Maintenant, utilisez votre classe dans app_dev.php (ou tout fichier d'index que vous utilisez)

// 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);
4
carmel

J'utilise actuellement la solution suivante I trouvée dans le groupe google AngularJS.

 $ http 
. post ('/ echo/json /', 'json =' + encodeURIComponent (angular.toJson (data)), {
 en-têtes: {
 'Content-Type': 'application/x-www-form-urlencoded; charset = UTF-8' 
} 
}) Success (function (data) {
 $ scope.data = data; 
}); 

Notez que si vous utilisez PHP, vous devez utiliser quelque chose comme Request::createFromGlobals() du composant HTTP de Symfony 2 pour lire ceci, car $ _POST ne sera pas chargé automatiquement.

3
Aditya M P

Il suffit de définir Content-Type ne suffit pas, url encoder les données du formulaire avant l'envoi. $http.post(url, jQuery.param(data))

3
Merlin Ran

Réponse complète (depuis angular 1.4). Vous devez inclure la dépendance $ httpParamSerializer

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) { 

            });
2
Sebastián Rojas

AngularJS le fait correctement en faisant le type de contenu suivant dans l'en-tête de la requête http:

Content-Type: application/json

Si vous utilisez php comme moi, ou même Symfony2, vous pouvez simplement étendre la compatibilité de votre serveur pour le standard JSON comme décrit ici: http://silex.sensiolabs.org/doc/cookbook/json_request_body.html

La manière Symfony2 (par exemple, dans votre contrôleur par défaut):

$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());

L’avantage, c’est que vous n’avez pas besoin d’utiliser jQuery param et que vous pouvez utiliser AngularJS comme moyen natif de faire de telles requêtes.

2
Michael

Dans votre configuration d'application -

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

Avec votre demande de ressource -

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

Ce n'est pas une réponse directe, mais plutôt une direction de conception légèrement différente:

N'envoyez pas les données sous forme de formulaire, mais sous forme d'objet JSON devant être directement mappé sur un objet côté serveur, ou utilisez la variable de style REST

Maintenant, je sais qu’aucune des deux options ne peut convenir à votre cas puisque vous essayez de passer une clé XSRF. Le mapper dans une variable de chemin comme celle-ci est une conception terrible:

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

Parce que, par nature, vous voudriez aussi passer la clé xsrf à un autre chemin, /login, /book-appointment etc. et vous ne voulez pas gâcher votre jolie URL

Il est intéressant de l'ajouter en tant que champ d'objet n'est pas approprié non plus, car maintenant, sur chaque objet json que vous transmettez au serveur, vous devez ajouter le champ.

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

Vous ne voudrez certainement pas ajouter un autre champ sur votre classe côté serveur qui ne possède pas d'association sémantique directe avec l'objet de domaine.

À mon avis, le meilleur moyen de passer votre clé xsrf est via un en-tête HTTP. De nombreuses bibliothèques d'infrastructure Web côté protection xsrf le prennent en charge. Par exemple, dans Java Spring, vous pouvez le transmettre à l'aide de X-CSRF-TOKEN en-tête .

L’excellente capacité d’Angular de lier un objet JS à un objet d’UI signifie que nous pouvons nous débarrasser de la pratique de l’envoi de formulaires en bloc et de l’envoi de JSON. JSON peut être facilement désérialisé en objet côté serveur et prend en charge des structures de données complexes telles que des cartes, des tableaux, des objets imbriqués, etc.

Comment postez-vous tableau dans une charge de formulaire? Peut-être comme ça:

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

ou ca:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday

Les deux sont de mauvaise conception ..

0
gerrytan

C’est ce que je fais pour mon besoin: je dois envoyer les données de connexion à l’API sous forme de données de formulaire et l’objet Javascript (userData) est automatiquement converti en données encodées en URL.

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

Voici comment mes données utilisateur sont

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