Publicación HTTP de AngularJS en PHP y no definido

107

Tengo un formulario con la etiqueta ng-submit="login()

La función se llama bien 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');
        });
    }
}

Recibo una respuesta 200 OK del archivo PHP, sin embargo, los datos devueltos dicen eso emaily passwordno están definidos. Este es todo el php que tengo

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

¿Alguna idea de por qué obtengo POSTvalores indefinidos ?

EDITAR

Quería señalar ya que esta parece ser una pregunta popular (pero es antigua), .successy .errorha sido obsoleta y debería usarla .thencomo @James Gentes señaló en los comentarios.

Ronnie
fuente
2
¿Miraste la pestaña de red de tus herramientas de desarrollador? ¿Qué valor se transfiere $http?
Marcel Korpel
1
En la pestaña de red, debajo Form-Datadice{"email":"[email protected]","password":"1234"}
Ronnie
@Ronnie Parece JSON. Prueba print_r($_POST);y luego prueba json_decode()con el índice correcto
HamZa
1
echo 'test';funciona bien. Ciertamente estoy señalando el archivo correcto
Ronnie
1
Tenga en cuenta que .success y .error han quedado obsoletos y reemplazados por .then ( docs.angularjs.org/api/ng/service/$http )
James Gentes

Respuestas:

228

angularjs .post()predetermina el encabezado de tipo de contenido a application/json. Está anulando esto para pasar datos codificados por formulario, sin embargo, no está cambiando su datavalor para pasar una cadena de consulta adecuada, por lo que PHP no se está completando $_POSTcomo esperaba.

Mi sugerencia sería usar la configuración predeterminada de angularjs application/jsoncomo encabezado, leer la entrada sin procesar en PHP y luego deserializar el JSON.

Eso se puede lograr en PHP de esta manera:

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

Alternativamente, si confía en gran medida en la $_POSTfuncionalidad, puede formar una cadena de consulta como [email protected]&password=somepasswordy enviarla como datos. Asegúrese de que esta cadena de consulta esté codificada como URL. Si se construye manualmente (en lugar de usar algo como jQuery.serialize()), Javascript encodeURIComponent()debería hacer el truco por usted.

Mike Brant
fuente
7
Sin faltarle el respeto a tu conocimiento, pero usarlo file_get_contents("php://input");parece una especie de truco, ¿no? Nunca he oído hablar de esto. Lo que debe suceder para que pueda hacer referencia a él como$_POST['email'];
Ronnie
6
@Ronnie No es un truco. Realmente depende de cómo desee configurar su servicio web. Si desea enviar y recuperar JSON, debe trabajar con la entrada sin procesar, ya $_POSTque no se completará.
Mike Brant
1
@lepe No me queda claro cómo se relaciona la pregunta / respuesta vinculada con mi respuesta. Aquí no hay discusión sobre la necesidad de serializar un objeto javascript.
Mike Brant
2
@lascort, realmente no hay mucha diferencia en las soluciones. En mi solución, no estoy completando los datos en $ _POST, prefiero una variable definida por el usuario. En general, esto tiene más sentido para mí, ya que cuando trabaja con datos serializados JSON, es posible que esté trabajando con objetos o matrices indexadas numéricamente. No sugeriría agregar una matriz indexada numéricamente a $ _POST, ya que este sería un uso atípico. En general, también evito poner datos en cualquiera de las superglobales que se utilizan para los datos de entrada.
Mike Brant
1
@ItsmeJulian no hay una respuesta correcta absoluta. Realmente podría depender de cómo esté diseñada su aplicación. Si está interactuando con un servicio REST que consume / entrega JSON, probablemente me quedaría con el tipo de contenido JSON. Si estuviera trabajando en gran medida con un punto final que pueda producir HTML o fragmentos HTML o use la publicación de formularios básicos como respaldo a AJAX, entonces podría estar inclinado a seguir con la codificación de formularios.
Mike Brant
41

Lo hago en el lado del servidor, al comienzo de mi archivo de inicio, funciona como un encanto y no tienes que hacer nada en el código php angular o existente:

if ($_SERVER['REQUEST_METHOD'] == 'POST' && empty($_POST))
    $_POST = json_decode(file_get_contents('php://input'), true);
Valmarv
fuente
3
que debe emitir su resultado en el caso de $ _POST está vacía: $_POST = (array) json_decode(file_get_contents('php://input'), true).
M'sieur Toph '12 de
Realmente no entiendo por qué los chicos de PHP no implementaron esto ellos mismos, ¿realmente esperan publicaciones codificadas en URL siempre?
azerafati
@Bludream No entiendo por qué alguien espera que PHP haga lo que se espera. Viola el principio de mínima sorpresa todo el tiempo.
doug65536
Esto parece un enfoque muy frágil a menos que sepa absolutamente que los datos POSTed representan una matriz asociativa (una representación de objeto en JSON). ¿Qué pasa si el JSON contiene una representación de matriz indexada numéricamente? $_POSTterminaría obteniendo una matriz indexada numéricamente en estos casos, algo que sería un comportamiento muy inesperado en otra parte del sistema que se basa en un $_POSTcomportamiento superglobal consistente .
Mike Brant
1
Solo uso esto en aplicaciones de potencia angular, por supuesto, y valido mis datos POST muy a fondo, no veo cómo puede ser esto un problema ...
valmarv
14

En la API que estoy desarrollando tengo un controlador base y dentro de su método __construct () tengo lo siguiente:

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

Esto me permite simplemente hacer referencia a los datos json como $ _POST ["var"] cuando sea necesario. Funciona genial.

De esa manera, si un usuario autenticado se conecta con una biblioteca como jQuery que envía datos de publicación con un valor predeterminado de Content-Type: application / x-www-form-urlencoded o Content-Type: application / json, la API responderá sin errores y hacer que la API sea un poco más amigable para los desarrolladores.

Espero que esto ayude.

Tim Wickstrom
fuente
11

Debido a que PHP no acepta JSON de forma nativa, 'application/json'un enfoque es actualizar sus encabezados y parámetros desde angular para que su api pueda usar los datos directamente.

Primero , parametrice sus datos:

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

Luego , agregue lo siguiente a su$http

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

Si todas sus solicitudes van a PHP, los parámetros se pueden establecer globalmente en la configuración de la siguiente manera:

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

Código de demostración de 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);
        });

        }]);

Código del lado del servidor: -

<?php


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

?>

Debido al comportamiento angular, no existe un método directo para el comportamiento de publicación normal en el servidor PHP, por lo que debe administrarlo en objetos json.

Rajendra Khabiya
fuente
1
Esto es incorrecto. Vea las respuestas anteriores donde JSON se lee directamente desde la entrada sin procesar de PHP. Esto es mucho más sencillo que mezclar JSON en una cadena de consulta.
Mike Brant
Los métodos .successy .errorestán en desuso y se han eliminado del marco AngularJS.
georgeawg
6

Necesita deserializar los datos de su formulario antes de pasarlos como segundo parámetro a .post (). Puede lograr esto usando el método $ .param (data) de jQuery. Entonces podrá en el lado del servidor hacer referencia a él como $ .POST ['email'];

bahramzy
fuente
6

Esta es la mejor solución (IMO) ya que no requiere jQuery ni decodificación JSON:

Fuente: https://wordpress.stackexchange.com/a/179373 y: https://stackoverflow.com/a/1714899/196507

Resumen:

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

Ejemplo:

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

Código PHP:

<?php
    $id = $_POST["id"];
?>
lepe
fuente
¿Qué pasa con: JSON.stringify('{ id: 'some_id', name : 'some_name' }')o $httpParamSerializerpara la conversión del lado del cliente?
qrtLs
5

Es una pregunta antigua, pero vale la pena mencionar que en Angular 1.4 se agrega $ httpParamSerializer y cuando usamos $ http.post, si usamos $ httpParamSerializer (params) para pasar los parámetros, todo funciona como una solicitud de publicación normal y no se deserializa JSON necesario en el lado del servidor.

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

sep
fuente
1

Me tomó horas entender eso mientras trabajaba con Angular y PHP. Los datos de publicación no iban a PHP en $ _POST

en código PHP, haga lo siguiente. - Cree una variable $ angular_post_params - Luego haga lo siguiente $angular_http_params = (array)json_decode(trim(file_get_contents('php://input')));

ahora puede acceder a sus parámetros como lo hace desde $ _POST

$angular_http_params["key"]

en caso de que se esté preguntando acerca de javascript ... esto es lo que usé

    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
fuente
para principiantes ... no se olviden de agregar las directivas ng-app y ng-controller a su contenedor de contenido en la página :)
Talha
Los métodos .successy .errorestán en desuso y se han eliminado del marco AngularJS.
georgeawg