AngularJS HTTP post para PHP e undefined

107

Eu tenho um formulário com a tag ng-submit="login()

A função é chamada bem em 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');
        });
    }
}

Estou recebendo uma resposta 200 OK do arquivo PHP, no entanto, os dados retornados dizem isso emaile passwordsão indefinidos. Este é todo o php que tenho

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

Alguma ideia de por que estou recebendo POSTvalores indefinidos ?

EDITAR

Eu queria apontar, já que esta parece ser uma pergunta popular (embora seja antiga), .successe .errorfoi descontinuada e você deve usar .thencomo @James Gentes apontou nos comentários

Ronnie
fonte
2
Você olhou para a guia de rede de suas ferramentas de desenvolvedor? Qual valor é passado $http?
Marcel Korpel
1
Na guia da rede, abaixo Form-Datadiz{"email":"[email protected]","password":"1234"}
Ronnie
@Ronnie parece JSON. Experimente print_r($_POST);e experimente json_decode()o índice certo
HamZa
1
echo 'test';funciona bem. Estou certamente apontando para o arquivo certo
Ronnie
1
Observe que .success e .error foram descontinuados e substituídos por .then ( docs.angularjs.org/api/ng/service/$http )
James Gentes

Respostas:

228

angularjs .post() padroniza o cabeçalho do tipo de conteúdo para application/json. Você está substituindo isso para passar dados codificados por formulário, no entanto, não está mudando seu datavalor para passar uma string de consulta apropriada, portanto, o PHP não está preenchendo $_POSTconforme o esperado.

Minha sugestão seria apenas usar a configuração angularjs padrão de application/jsoncomo cabeçalho, ler a entrada bruta em PHP e desserializar o JSON.

Isso pode ser conseguido em PHP assim:

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

Como alternativa, se você depende muito da $_POSTfuncionalidade, pode formar uma string de consulta como [email protected]&password=somepassworde enviá-la como dados. Certifique-se de que esta string de consulta seja codificada por URL. Se construído manualmente (ao invés de usar algo como jQuery.serialize()), o Javascript encodeURIComponent()deve fazer o truque para você.

Mike Brant
fonte
7
Sem desrespeitar o seu conhecimento, mas usar file_get_contents("php://input");parece meio que um hack, não? Eu nunca ouvi falar disso O que precisa acontecer para que eu possa apenas fazer referência a$_POST['email'];
Ronnie
6
@Ronnie Não é um hack. Realmente depende de como você deseja configurar seu serviço da web. Se você deseja enviar e recuperar JSON, você precisa trabalhar com entrada bruta, pois $_POSTnão será preenchida.
Mike Brant
1
@lepe Não está claro para mim como a pergunta / resposta vinculada se relaciona com a minha resposta. Não há discussão sobre a necessidade de serializar um objeto javascript aqui.
Mike Brant,
2
@lascort realmente não há muita diferença nas soluções. Em minha solução, não estou preenchendo os dados em $ _POST, preferindo uma variável definida pelo usuário. Em geral, isso faz mais sentido para mim, pois ao trabalhar com dados serializados JSON, você pode estar trabalhando com objetos ou matrizes indexadas numericamente. Eu não sugeriria adicionar uma matriz indexada numericamente a $ _POST, pois isso seria um uso atípico. Eu também geralmente evito colocar dados em qualquer uma das superglobais que são usadas para dados de entrada.
Mike Brant
1
@ItsmeJulian, não existe uma resposta absolutamente certa. Pode realmente depender de como seu aplicativo é arquitetado. Se você estiver interagindo com um serviço REST que está consumindo / entregando JSON, então provavelmente ficaria com o tipo de conteúdo JSON. Se eu estivesse trabalhando principalmente com um endpoint que pode produzir HTML ou fragmentos HTML ou usa postagem de formulário básico como fallback para AJAX, então eu poderia ficar inclinado a ficar com a codificação de formulário.
Mike Brant
41

Eu faço isso no lado do servidor, no início do meu arquivo init, funciona perfeitamente e você não precisa fazer nada no código PHP angular ou existente:

if ($_SERVER['REQUEST_METHOD'] == 'POST' && empty($_POST))
    $_POST = json_decode(file_get_contents('php://input'), true);
Valmarv
fonte
3
você deve lançar o seu resultado no caso $ _POST está vazio: $_POST = (array) json_decode(file_get_contents('php://input'), true).
M'sieur Toph '12 de
Eu realmente não entendo por que os caras do PHP não implementaram isso eles mesmos. Eles realmente esperam sempre posts codificados por url ??
azerafati
@Bludream Não entendo por que alguém espera que o PHP faça o que é esperado. Isso viola o princípio da surpresa mínima o tempo todo.
doug65536
Esta parece ser uma abordagem muito frágil, a menos que você saiba com certeza que os dados POSTed representam uma matriz associativa (uma representação de objeto em JSON). E se o JSON contivesse uma representação de matriz indexada numericamente? $_POSTacabaria obtendo um array indexado numericamente nesses casos, algo que seria um comportamento muito inesperado em outra parte do sistema que depende de um $_POSTcomportamento superglobal consistente .
Mike Brant,
1
Eu só uso isso em aplicativos movidos a angular, é claro, e valido meus dados de POST completamente, não vejo como isso pode ser um problema ...
valmarv
14

Na API que estou desenvolvendo, tenho um controlador de base e dentro de seu método __construct () tenho o seguinte:

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

Isso me permite simplesmente fazer referência aos dados json como $ _POST ["var"] quando necessário. Funciona bem.

Dessa forma, se um usuário autenticado se conectar a uma biblioteca como uma jQuery que envia dados de postagem com um padrão de Content-Type: application / x-www-form-urlencoded ou Content-Type: application / json, a API responderá sem erros e tornar a API um pouco mais amigável para o desenvolvedor.

Espero que isto ajude.

Tim Wickstrom
fonte
11

Porque o PHP não aceita nativamente JSON 'application/json' uma abordagem é atualizar seus cabeçalhos e parâmetros do angular para que sua API possa usar os dados diretamente.

Primeiro , parametrize seus dados:

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

Em seguida , adicione o seguinte ao seu$http

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

Se todas as suas solicitações forem para PHP, os parâmetros podem ser definidos globalmente na configuração da seguinte maneira:

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

Código de demonstração 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 do lado do servidor: -

<?php


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

?>

Devido ao comportamento angular, não existe um método direto para o comportamento normal de postagem no servidor PHP, então você deve gerenciá-lo em objetos json.

Rajendra Khabiya
fonte
1
Isso está incorreto. Veja as respostas acima, onde JSON é lido diretamente da entrada bruta do PHP. Isso é muito mais simples do que misturar JSON em string de consulta.
Mike Brant
Os métodos .successe .errorestão obsoletos e foram removidos da estrutura AngularJS.
georgeawg
6

Você precisa desserializar os dados do formulário antes de passá-los como o segundo parâmetro para .post (). Você pode conseguir isso usando o método $ .param (data) do jQuery. Então você será capaz de fazer referência no lado do servidor como $ .POST ['email'];

Bahramzy
fonte
6

Esta é a melhor solução (IMO), pois não requer jQuery e nenhuma decodificação JSON:

Fonte: https://wordpress.stackexchange.com/a/179373 e: https://stackoverflow.com/a/1714899/196507

Resumo:

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

Exemplo:

...
   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
fonte
Que tal: JSON.stringify('{ id: 'some_id', name : 'some_name' }')ou $httpParamSerializerpara conversão do lado do cliente?
qrtLs
5

É uma pergunta antiga, mas vale a pena mencionar que no Angular 1.4 $ httpParamSerializer é adicionado e ao usar $ http.post, se usarmos $ httpParamSerializer (params) para passar os parâmetros, tudo funciona como uma solicitação de postagem normal e nenhuma desserialização de JSON é necessário no lado do servidor.

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

Set
fonte
1

Levei horas para entender isso enquanto trabalhava com Angular e PHP. Os dados da postagem não iam para o PHP em $ _POST

no código PHP, faça o seguinte. - Crie uma variável $ angular_post_params - Em seguida, faça o seguinte $angular_http_params = (array)json_decode(trim(file_get_contents('php://input')));

agora você pode acessar seus parâmetros como faz em $ _POST

$angular_http_params["key"]

caso você esteja se perguntando sobre javascript .... isso é o que eu usei

    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
fonte
para iniciantes ... não se esqueça de adicionar as diretivas ng-app e ng-controller ao seu contêiner de suporte de conteúdo na página :)
Talha
Os métodos .successe .errorestão obsoletos e foram removidos da estrutura AngularJS.
georgeawg