AngularJS HTTP wysyła do PHP i jest niezdefiniowany

107

Mam formularz z tagiem ng-submit="login()

Funkcja jest nazywana w javascript dobrze.

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

Otrzymuję odpowiedź 200 OK z powrotem z pliku PHP, jednak zwrócone dane mówią o tym emaili passwordsą niezdefiniowane. To wszystko, co mam php

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

Masz jakiś pomysł, dlaczego otrzymuję niezdefiniowane POSTwartości?

EDYTOWAĆ

Chciałem zwrócić uwagę, ponieważ wydaje się, że jest to popularne pytanie (ale jest stare) .successi .errorzostało wycofane i powinieneś używać, .thenjak wskazał @James Gentes w komentarzach

Ronnie
źródło
2
Czy spojrzałeś na kartę sieciową swoich narzędzi programistycznych? Jaka wartość jest przekazywana $http?
Marcel Korpel
1
W zakładce sieci pod Form-Datanim jest napisane{"email":"[email protected]","password":"1234"}
Ronnie
@Ronnie Wygląda na JSON. Spróbuj, print_r($_POST);a potem spróbuj json_decode()na prawym indeksie
HamZa
1
echo 'test';działa w porządku. Z pewnością wskazuję na właściwy plik
Ronnie
1
Zauważ, że .success i .error zostały wycofane i zastąpione przez .then ( docs.angularjs.org/api/ng/service/$http )
James Gentes,

Odpowiedzi:

228

angularjs .post()domyślnie ustawia nagłówek Content-Type na application/json. Zastępujesz to, aby przekazywać dane zakodowane w postaci, jednak nie zmieniasz datawartości, aby przekazać odpowiedni ciąg zapytania, więc PHP nie zapełnia się $_POSTzgodnie z oczekiwaniami.

Moja sugestia byłaby taka, aby po prostu użyć domyślnego ustawienia angularjs application/jsonjako nagłówek as, przeczytać nieprzetworzone dane wejściowe w PHP, a następnie deserializować JSON.

Można to osiągnąć w PHP w następujący sposób:

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

Alternatywnie, jeśli w dużym stopniu polegasz na $_POSTfunkcjonalności, możesz utworzyć ciąg zapytania, taki jak [email protected]&password=somepasswordi wysłać go jako dane. Upewnij się, że ten ciąg zapytania jest zakodowany w postaci adresu URL. Jeśli jest zbudowany ręcznie (w przeciwieństwie do czegoś podobnego jQuery.serialize()), Javascript encodeURIComponent()powinien załatwić sprawę za Ciebie.

Mike Brant
źródło
7
Nie lekceważąc Twojej wiedzy, ale używanie file_get_contents("php://input");wydaje się trochę jak hack, prawda? Nigdy o tym nie słyszałem. Co musi się wydarzyć, abym mógł się do tego odnieść w stylu$_POST['email'];
Ronnie
6
@Ronnie To nie jest hack. To naprawdę zależy od tego, jak chcesz skonfigurować usługę internetową. Jeśli chcesz wysyłać i pobierać dane JSON, musisz pracować z nieprzetworzonymi danymi wejściowymi, ponieważ $_POSTnie zostaną wypełnione.
Mike Brant
1
@lepe Nie jest dla mnie jasne, jak powiązane pytanie / odpowiedź odnosi się do mojej odpowiedzi. Nie ma tutaj dyskusji o konieczności serializacji obiektu javascript.
Mike Brant
2
@lascort naprawdę nie ma dużej różnicy w rozwiązaniach. W moim rozwiązaniu nie wypełniam danych w $ _POST, wolę zamiast tego zmienną zdefiniowaną przez użytkownika. Ogólnie rzecz biorąc, ma to dla mnie większy sens, ponieważ podczas pracy z danymi serializowanymi JSON możesz pracować z obiektami lub tablicami indeksowanymi numerycznie. Nie sugerowałbym dodawania tablicy indeksowanej numerycznie do $ _POST, ponieważ byłoby to nietypowe użycie. Generalnie unikam też umieszczania danych w żadnym z superglobali, które są używane do wprowadzania danych.
Mike Brant
1
@ItsmeJulian nie ma absolutnie dobrej odpowiedzi. Może to naprawdę zależeć od tego, jak zaprojektowana jest Twoja aplikacja. Jeśli wchodzisz w interakcję z usługą REST, która konsumuje / dostarcza JSON, prawdopodobnie pozostałbym przy typie zawartości JSON. Jeśli pracowałem w dużej mierze z punktem końcowym, który może tworzyć fragmenty HTML lub HTML lub używa podstawowego wysyłania formularzy jako rozwiązania zastępczego do AJAX, mógłbym być skłonny trzymać się kodowania formularzy.
Mike Brant
41

Robię to po stronie serwera, na początku mojego pliku init, działa jak urok i nie musisz nic robić w kątowym lub istniejącym kodzie php:

if ($_SERVER['REQUEST_METHOD'] == 'POST' && empty($_POST))
    $_POST = json_decode(file_get_contents('php://input'), true);
valmarv
źródło
3
oddasz swój wynik w przypadku $ _POST jest pusty: $_POST = (array) json_decode(file_get_contents('php://input'), true).
M'sieur Toph '12
Naprawdę nie rozumiem, dlaczego faceci z PHP nie zaimplementowali tego sami. Naprawdę oczekują zawsze postów z kodowaniem url?
azerafati
@Bludream Nie rozumiem, dlaczego ktokolwiek oczekuje, że PHP robi to, czego się oczekuje. Cały czas narusza zasadę najmniejszego zaskoczenia.
doug65536
Wydaje się, że jest to bardzo delikatne podejście, chyba że absolutnie wiesz, że dane POST reprezentują tablicę asocjacyjną (reprezentację obiektu w formacie JSON). A co, jeśli JSON zawiera numerycznie indeksowaną reprezentację tablicy? $_POSTw takich przypadkach skończyłoby się otrzymaniem tablicy indeksowanej numerycznie, co byłoby bardzo nieoczekiwanym zachowaniem w innej części systemu, która opiera się na spójnym $_POSTzachowaniu superglobalnym.
Mike Brant
1
Oczywiście używam tego tylko w aplikacjach zasilanych kątowo i bardzo dokładnie weryfikuję moje dane POST, nie rozumiem, jak to może być problem ...
valmarv
14

W API, które tworzę, mam kontroler podstawowy, a wewnątrz jego metody __construct () mam:

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

To pozwala mi w razie potrzeby po prostu odwoływać się do danych json jako $ _POST ["var"]. Działa świetnie.

W ten sposób, jeśli uwierzytelniony użytkownik połączy się z biblioteką, taką jak jQuery, która wysyła dane postu z domyślnym Content-Type: application / x-www-form-urlencoded lub Content-Type: application / json, API odpowie bez błędów i uczynić API trochę bardziej przyjaznym dla programistów.

Mam nadzieję że to pomoże.

Tim Wickstrom
źródło
11

Ponieważ PHP natywnie nie akceptuje formatu JSON, 'application/json'jednym z podejść jest aktualizacja nagłówków i parametrów z angular, aby interfejs API mógł bezpośrednio używać danych.

Najpierw sparametryzuj swoje dane:

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

Następnie dodaj następujące elementy do swojego$http

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

Jeśli wszystkie żądania idą do PHP, parametry można ustawić globalnie w konfiguracji w następujący sposób:

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

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

        }]);

Kod po stronie serwera: -

<?php


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

?>

Ze względu na zachowanie kątowe nie ma bezpośredniej metody normalnego zachowania postów na serwerze PHP, więc musisz zarządzać nimi w obiektach json.

Rajendra Khabiya
źródło
1
To jest niepoprawne. Zobacz odpowiedzi powyżej, gdzie JSON jest odczytywany bezpośrednio z surowego wejścia PHP. Jest to znacznie prostsze niż mieszanie JSON w ciągu zapytania.
Mike Brant
Te .successi .errormetody są wycofywane i usunięto z ramy angularjs.
georgeawg
6

Musisz deserializować dane formularza przed przekazaniem ich jako drugiego parametru do .post (). Możesz to osiągnąć za pomocą metody $ .param (data) jQuery. Wtedy będziesz mógł odwoływać się do niego po stronie serwera jak $ .POST ['email'];

bahramzy
źródło
6

To najlepsze rozwiązanie (IMO), ponieważ nie wymaga jQuery ani dekodowania JSON:

Źródło: https://wordpress.stackexchange.com/a/179373 i: https://stackoverflow.com/a/1714899/196507

Podsumowanie:

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

Przykład:

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

Kod PHP:

<?php
    $id = $_POST["id"];
?>
lepe
źródło
A co z: JSON.stringify('{ id: 'some_id', name : 'some_name' }')czy w $httpParamSerializerprzypadku konwersji po stronie klienta?
qrtLs
5

To stare pytanie, ale warto wspomnieć, że w Angular 1.4 dodano $ httpParamSerializer, a gdy używamy $ http.post, jeśli użyjemy $ httpParamSerializer (params) do przekazania parametrów, wszystko działa jak zwykłe żądanie posta i nie ma deserializacji JSON potrzebne po stronie serwera.

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

Wrz
źródło
1

Zrozumienie tego zajęło mi wiele godzin podczas pracy z Angularem i PHP. Dane postu nie trafiały do ​​PHP w $ _POST

w kodzie PHP wykonaj następujące czynności. - Utwórz zmienną $ angular_post_params - Następnie wykonaj następujące czynności $angular_http_params = (array)json_decode(trim(file_get_contents('php://input')));

teraz możesz uzyskać dostęp do swoich parametrów, tak jak robisz to z $ _POST

$angular_http_params["key"]

w przypadku, gdy zastanawiałeś się nad javascript .... to jest to, czego użyłem

    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
źródło
dla początkujących ... nie zapomnij mieć dyrektyw NG-app i NG-kontroler dodany do zawartości pojemnika uchwytem na stronie :)
Talha
Te .successi .errormetody są wycofywane i usunięto z ramy angularjs.
georgeawg