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() {}
});
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
Pour utiliser les nouveaux services ajoutés avec AngularJS V1.4, voir
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 () {});
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.
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("&");
});
}]);
À 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);
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
});
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
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) {
//...
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 */
});
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)
})
}
})
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.
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
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);
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.
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))
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) {
});
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.
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'
}
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 ..
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
}