Publication HTTP AngularJS vers PHP et non définie

107

J'ai un formulaire avec le tag ng-submit="login()

La fonction est appelée correctement en javascript.

function LoginForm($scope, $http)
{
    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';

    $scope.email    = "[email protected]";
    $scope.password = "1234";

    $scope.login = function()
    {
        data = {
            'email' : $scope.email,
            'password' : $scope.password
        };

        $http.post('resources/curl.php', data)
        .success(function(data, status, headers, config)
        {
            console.log(status + ' - ' + data);
        })
        .error(function(data, status, headers, config)
        {
            console.log('error');
        });
    }
}

Je reçois une réponse 200 OK du fichier PHP, cependant, les données renvoyées le disent emailet ne passwordsont pas définies. C'est tout le php que j'ai

<?php
$email = $_POST['email'];
$pass  = $_POST['password'];
echo $email;
?>

Une idée de pourquoi j'obtiens des POSTvaleurs indéfinies ?

ÉDITER

Je voulais le souligner car cela semble être une question populaire (mais elle est ancienne), .successet .errora été obsolète et vous devriez utiliser .thencomme @James Gentes l'a souligné dans les commentaires

Ronnie
la source
2
Avez-vous regardé l'onglet réseau de vos outils de développement? Quelle valeur est transmise $http?
Marcel Korpel
1
Dans l'onglet réseau, en dessous Form-Datail est dit{"email":"[email protected]","password":"1234"}
Ronnie
@Ronnie On dirait JSON. Essayez print_r($_POST);puis essayez json_decode()le bon index
HamZa
1
echo 'test';fonctionne très bien. Je pointe certainement vers le bon fichier
Ronnie
1
Notez que .success et .error ont été déconseillés et remplacés par .then ( docs.angularjs.org/api/ng/service/$http )
James Gentes

Réponses:

228

angularjs définit par .post()défaut l'en-tête Content-type sur application/json. Vous remplacez cela pour transmettre des données codées par formulaire, mais vous ne modifiez pas votre datavaleur pour transmettre une chaîne de requête appropriée, de sorte que PHP ne se remplit pas $_POSTcomme prévu.

Ma suggestion serait d'utiliser simplement le paramètre angularjs par défaut application/jsoncomme en-tête, de lire l'entrée brute en PHP, puis de désérialiser le JSON.

Cela peut être réalisé en PHP comme ceci:

$postdata = file_get_contents("php://input");
$request = json_decode($postdata);
$email = $request->email;
$pass = $request->password;

Sinon, si vous comptez beaucoup sur la $_POSTfonctionnalité, vous pouvez former une chaîne de requête comme [email protected]&password=somepasswordet l'envoyer sous forme de données. Assurez-vous que cette chaîne de requête est encodée en URL. S'il est construit manuellement (au lieu d'utiliser quelque chose comme jQuery.serialize()), Javascript encodeURIComponent()devrait faire l'affaire pour vous.

Mike Brant
la source
7
Ne pas manquer de respect à vos connaissances, mais utiliser file_get_contents("php://input");semble un peu comme un hack, non? Je n'en ai jamais entendu parler. Ce qui doit se passer pour que je puisse simplement le référencer comme$_POST['email'];
Ronnie
6
@Ronnie Ce n'est pas un hack. Cela dépend vraiment de la façon dont vous souhaitez configurer votre service Web. Si vous souhaitez envoyer et récupérer du JSON, vous devez travailler avec une entrée brute car $_POSTelle ne sera pas remplie.
Mike Brant
1
@lepe Je ne vois pas clairement comment la question / réponse liée à ma réponse n'est pas claire. Il n'y a pas de discussion sur la nécessité de sérialiser un objet javascript ici.
Mike Brant
2
@lascort il n'y a vraiment pas beaucoup de différence entre les solutions. Dans ma solution, je ne remplis pas les données dans $ _POST, préférant plutôt une variable définie par l'utilisateur. En général, cela a plus de sens pour moi dans la mesure où lorsque vous travaillez avec des données sérialisées JSON, vous pouvez travailler avec des objets ou des tableaux indexés numériquement. Je ne suggérerais pas d'ajouter un tableau indexé numériquement à $ _POST, car ce serait une utilisation atypique. J'ai également généralement peur de mettre des données dans l'une des superglobales utilisées pour les données d'entrée.
Mike Brant
1
@ItsmeJulian il n'y a pas de bonne réponse absolue. Cela peut vraiment dépendre de l'architecture de votre application. Si vous interagissez avec un service REST qui consomme / fournit du JSON, je m'en tiendrai probablement au type de contenu JSON. Si je travaillais en grande partie avec un point de terminaison capable de produire des fragments HTML ou HTML ou d'utiliser la publication de formulaire de base comme solution de secours à AJAX, alors je pourrais être enclin à m'en tenir à l'encodage de formulaire.
Mike Brant
41

Je le fais côté serveur, au début de mon fichier init, fonctionne comme un charme et vous n'avez rien à faire dans le code php angulaire ou existant:

if ($_SERVER['REQUEST_METHOD'] == 'POST' && empty($_POST))
    $_POST = json_decode(file_get_contents('php://input'), true);
Valmarv
la source
3
vous devriez jeté votre résultat en cas $ _POST est vide: $_POST = (array) json_decode(file_get_contents('php://input'), true).
M'sieur Toph '12
Je ne comprends pas vraiment pourquoi les gars de PHP n'ont pas implémenté cela eux-mêmes, ils s'attendent vraiment à des messages encodés en URL toujours ??
azerafati
@Bludream Je ne comprends pas pourquoi quelqu'un s'attend à ce que PHP fasse ce qui est attendu. Cela viole toujours le principe de la moindre surprise.
doug65536
Cela semble être une approche très fragile à moins que vous ne sachiez absolument que les données POSTées représentent un tableau associatif (une représentation d'objet en JSON). Que faire si le JSON contenait une représentation de tableau à indexation numérique? $_POSTfinirait par obtenir un tableau indexé numériquement dans ce cas, ce qui serait un comportement très inattendu dans une autre partie du système qui repose sur un $_POSTcomportement super global cohérent .
Mike Brant
1
Je ne l'utilise que dans les applications angulaires, bien sûr, et je valide mes données POST de manière très approfondie, je ne vois pas en quoi cela peut être un problème ...
valmarv
14

Dans l'API que je développe, j'ai un contrôleur de base et dans sa méthode __construct (), j'ai ce qui suit:

if(isset($_SERVER["CONTENT_TYPE"]) && strpos($_SERVER["CONTENT_TYPE"], "application/json") !== false) {
    $_POST = array_merge($_POST, (array) json_decode(trim(file_get_contents('php://input')), true));
}

Cela me permet de simplement référencer les données json comme $ _POST ["var"] si nécessaire. Fonctionne très bien.

De cette façon, si un utilisateur authentifié se connecte à une bibliothèque telle que jQuery qui envoie des données de publication avec une valeur par défaut de Content-Type: application / x-www-form-urlencoded ou Content-Type: application / json, l'API répondra sans erreur et sera rendre l'API un peu plus conviviale pour les développeurs.

J'espère que cela t'aides.

Tim Wickstrom
la source
11

Parce que PHP n'accepte pas nativement JSON, 'application/json'une approche consiste à mettre à jour vos en-têtes et vos paramètres à partir d'angular afin que votre api puisse utiliser les données directement.

Tout d'abord , paramétrez vos données:

data: $.param({ "foo": $scope.fooValue })

Ensuite , ajoutez ce qui suit à votre$http

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

Si toutes vos requêtes vont à PHP, les paramètres peuvent être définis globalement dans la configuration comme suit:

myApp.config(function($httpProvider) {
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
});
Malkus
la source
8

Code de démonstration Angular Js: -

angular.module('ModuleName',[]).controller('main', ['$http', function($http){

                var formData = { password: 'test pwd', email : 'test email' };
                var postData = 'myData='+JSON.stringify(formData);
                $http({
                        method : 'POST',
                        url : 'resources/curl.php',
                        data: postData,
                        headers : {'Content-Type': 'application/x-www-form-urlencoded'}  

                }).success(function(res){
                        console.log(res);
                }).error(function(error){
                        console.log(error);
        });

        }]);

Code côté serveur: -

<?php


// it will print whole json string, which you access after json_decocde in php
$myData = json_decode($_POST['myData']);
print_r($myData);

?>

En raison du comportement angulaire, il n'y a pas de méthode directe pour le comportement de publication normal sur le serveur PHP, vous devez donc le gérer dans des objets json.

Rajendra Khabiya
la source
1
Ceci est une erreur. Voir les réponses ci-dessus où JSON est lu directement à partir de l'entrée brute PHP. C'est beaucoup plus simple que de mélanger JSON dans une chaîne de requête.
Mike Brant
Les méthodes .successet .errorsont obsolètes et ont été supprimées du framework AngularJS.
georgeawg
6

Vous devez désérialiser vos données de formulaire avant de les transmettre comme deuxième paramètre à .post (). Vous pouvez y parvenir en utilisant la méthode $ .param (data) de jQuery. Ensuite, vous pourrez du côté serveur de le référencer comme $ .POST ['email'];

Bahramzy
la source
6

C'est la meilleure solution (IMO) car elle ne nécessite aucun jQuery et aucun décodage JSON:

Source: https://wordpress.stackexchange.com/a/179373 et: https://stackoverflow.com/a/1714899/196507

Résumé:

//Replacement of jQuery.param
var serialize = function(obj, prefix) {
  var str = [];
  for(var p in obj) {
    if (obj.hasOwnProperty(p)) {
      var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
      str.push(typeof v == "object" ?
        serialize(v, k) :
        encodeURIComponent(k) + "=" + encodeURIComponent(v));
    }
  }
  return str.join("&");
};

//Your AngularJS application:
var app = angular.module('foo', []);

app.config(function ($httpProvider) {
    // send all requests payload as query string
    $httpProvider.defaults.transformRequest = function(data){
        if (data === undefined) {
            return data;
        }
        return serialize(data);
    };

    // set all post requests content type
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
});

Exemple:

...
   var data = { id: 'some_id', name : 'some_name' };
   $http.post(my_php_url,data).success(function(data){
        // It works!
   }).error(function() {
        // :(
   });

Code PHP:

<?php
    $id = $_POST["id"];
?>
lépe
la source
Qu'en est-il: JSON.stringify('{ id: 'some_id', name : 'some_name' }')ou $httpParamSerializerpour la conversion côté client?
qrtLs
5

C'est une vieille question mais il vaut la peine de mentionner que dans Angular 1.4 $ httpParamSerializer est ajouté et lors de l'utilisation de $ http.post, si nous utilisons $ httpParamSerializer (params) pour passer les paramètres, tout fonctionne comme une demande de publication régulière et aucune désérialisation JSON n'est nécessaire côté serveur.

https://docs.angularjs.org/api/ng/service/$httpParamSerializer

SEP
la source
1

Cela m'a pris des heures pour comprendre cela tout en travaillant avec Angular et PHP. Les données de publication n'allaient pas vers PHP dans $ _POST

dans le code PHP, procédez comme suit. - Créez une variable $ angular_post_params - Ensuite, procédez comme suit $angular_http_params = (array)json_decode(trim(file_get_contents('php://input')));

maintenant vous pouvez accéder à vos paramètres comme vous le faites depuis $ _POST

$angular_http_params["key"]

au cas où vous vous poseriez des questions sur javascript .... c'est ce que j'ai utilisé

    var myApp = angular.module('appUsers', []);
    //var post_params = $.param({ request_type: "getListOfUsersWithRolesInfo" });
    var dataObj = {
        task_to_perform: 'getListOfUsersWithRolesInfo'
    };

    myApp.controller('ctrlListOfUsers', function ($scope, $http) {
        $http({
            method: 'POST',
            dataType: 'json',
            url: ajax_processor_url,
            headers: {
                'Content-Type': 'application/json'
            },
            data: dataObj,
            //transformRequest: function(){},
            timeout: 30000,
            cache: false
        }).
        success(function (rsp) {
            console.log("success");
            console.log(rsp);
        }).
        error(function (rsp) {
            console.log("error");
        });
    });
Talha
la source
pour les débutants ... n'oubliez pas d'ajouter les directives ng-app et ng-controller à votre conteneur de contenu sur la page :)
Talha
Les méthodes .successet .errorsont obsolètes et ont été supprimées du framework AngularJS.
georgeawg