109

I have a form with the tag ng-submit="login()

The function gets called fine in javascript.

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

    $scope.email    = "fsdg@sdf.com";
    $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');
        });
    }
}

I am getting a 200 OK response back from the PHP file, however, the returned data is saying that email and password are undefined. This is all the php I have

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

Any idea why I am getting undefined POST values?

EDIT

I wanted to point out since this seems to be a popular question (yet it is old), .success and .error have been deprecated and you should use .then as @James Gentes pointed out in the commments

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Ronnie
  • 11,138
  • 21
  • 78
  • 140

9 Answers9

228

angularjs .post() defaults the Content-type header to application/json. You are overriding this to pass form-encoded data, however you are not changing your data value to pass an appropriate query string, so PHP is not populating $_POST as you expect.

My suggestion would be to just use the default angularjs setting of application/json as header, read the raw input in PHP, and then deserialize the JSON.

That can be achieved in PHP like this:

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

Alternately, if you are heavily relying on $_POST functionality, you can form a query string like email=someemail@email.com&password=somepassword and send that as data. Make sure that this query string is URL encoded. If manually built (as opposed to using something like jQuery.serialize()), Javascript's encodeURIComponent() should do the trick for you.

Mike Brant
  • 70,514
  • 10
  • 99
  • 103
  • 7
    Not disrespecting your knowledge, but using `file_get_contents("php://input");` seems kind of like a hack, no? I have never heard of this. What needs to happen so I can just reference it like `$_POST['email'];` – Ronnie Mar 18 '13 at 19:59
  • or was that the second part of your answer? – Ronnie Mar 18 '13 at 20:00
  • 6
    @Ronnie It is not a hack. It really depends on how you want to set up your web service. If you want to send and retrieve JSON, you need to work with raw input as `$_POST` will not be populated. – Mike Brant Mar 18 '13 at 20:03
  • ok, this makes sense. I am going to use your suggested solution. Thanks Mike and everyone else – Ronnie Mar 18 '13 at 20:05
  • @Ronnie: Don't forget to properly encode query string parameters using `encodeURIComponent`. E.g., `@` should be changed in `%40`. – Marcel Korpel Mar 18 '13 at 20:07
  • Thanks Marcel, I will keep that in mind, but I am not using the query string method. I am using the `file_get_contente(..etc);` solution. Mike's suggested solution – Ronnie Mar 18 '13 at 20:09
  • Serialize without JQuery: http://stackoverflow.com/questions/1714786/querystring-encoding-of-a-javascript-object – lepe Mar 11 '15 at 14:12
  • 1
    @lepe It is not clear to me how the linked question/answer relates to my answer. There is no discussion of needing to serialize a javascript object here. – Mike Brant Mar 18 '15 at 13:55
  • @MikeBrant: Thank you for pointing it out. You commented about "encodeURIComponent" and jQuery.serialize(), but it may be easier to show an example on how to achieve it without relying on jQuery. So that link complements your answer. – lepe Mar 24 '15 at 00:53
  • I guess I'll ask here since you seem to be more active.. what's the downside of using @valmarv 's answer over yours? (If there is any) – martskins Mar 03 '16 at 00:15
  • 2
    @lascort there is really not much of a difference to the solutions really. In my solution, I am not populating the data into $_POST, preferring a user-defined variable instead. In a general, this makes more sense to me in that when working with JSON-serialized data, you might be working wth objects, or numerically-indexed arrays. I wouldn't suggest adding a numerically-Indexed array to $_POST, as this would be an atypical use. I also generally shy away from putting data into any of the superglobals that are used for input data. – Mike Brant Mar 05 '16 at 04:21
  • maybe a mention of the `$maxlen` parameter to give developer more granular control of how much data it will load into memory for a given request. Especially if your server has a high max post size in the webserver or php daemon, granular control for each route can minimize the memory impact of malicious requests. – r3wt Dec 03 '16 at 14:01
  • @Ben10 This functionality should has nothing to do with SSL. You likely have a problem with either mixed SSL/non-SSL content, invalid/untrusted cert, or similar. – Mike Brant Dec 13 '16 at 14:49
  • Should one prefer `application/json` over the `urlencoded` Content-Type? I cannot decide as there is a angularjs `$httpParamSerializer` e.g. to help with conversion. – droid192 Dec 30 '16 at 10:59
  • 1
    @ItsmeJulian there is no absolute right answer. It might really depend on how your application is architected. If your are interacting with a REST service that is consuming/delivering JSON, then I would probably stick with JSON content type. If I were working largely with an endpoint the can produce HTML or HTML fragments or uses basic form posting as fallback to AJAX, then I might be inclined to stick with form encoding. – Mike Brant Jan 02 '17 at 15:18
41

I do it on the server side, at the begining of my init file, works like a charm and you don't have to do anything in angular or existing php code:

if ($_SERVER['REQUEST_METHOD'] == 'POST' && empty($_POST))
    $_POST = json_decode(file_get_contents('php://input'), true);
valmarv
  • 854
  • 1
  • 8
  • 14
  • 3
    you should cast your result in case $_POST is empty : `$_POST = (array) json_decode(file_get_contents('php://input'), true)`. – M'sieur Toph' Oct 12 '14 at 07:12
  • I don't really get why PHP guys didn't implement this themselves, They really expect url-encoded posts always?? – azerafati Aug 08 '15 at 10:40
  • @Bludream I don't get why anyone expects PHP to do what is expected. It violates the principle of least-surprise all the time. – doug65536 Aug 31 '15 at 12:00
  • This seems like a very fragile approach unless you absolutely know that the POSTed data represents an associative array (an object representation in JSON). What if the JSON contained a numerically-indexed array representation? `$_POST` would end up getting a numerically indexed array in this cases, something that would be very unexpected behavior in other part of the system that rely upon consistent `$_POST` superglobal behavior. – Mike Brant Oct 21 '15 at 20:55
  • 1
    I only use this in angular powered applications, of course, and I validate my POST data very thoroughly, don't see how can this be a problem... – valmarv Oct 22 '15 at 21:30
15

In the API I am developing I have a base controller and inside its __construct() method I have the following:

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

This allows me to simply reference the json data as $_POST["var"] when needed. Works great.

That way if an authenticated user connects with a library such a jQuery that sends post data with a default of Content-Type: application/x-www-form-urlencoded or Content-Type: application/json the API will respond without error and will make the API a little more developer friendly.

Hope this helps.

Tim Wickstrom
  • 5,476
  • 3
  • 25
  • 33
11

Because PHP does not natively accept JSON 'application/json' One approach is to update your headers and parameters from angular so that your api can use the data directly.

First, Parameterize your data:

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

Then, add the following to your $http

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

If all of your requests are going to PHP the parameters can be set globaly in the configuration as follows:

myApp.config(function($httpProvider) {
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
});
Malkus
  • 3,686
  • 2
  • 24
  • 39
8

Angular Js Demo Code :-

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

        }]);

Server Side Code :-

<?php


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

?>

Due to angular behaviour there is no direct method for normal post behaviour at PHP server, so you have to manage it in json objects.

Rajendra Khabiya
  • 1,990
  • 2
  • 22
  • 39
  • 1
    This is incorrect. See answers above where JSON is read directly from PHP raw input. This is much more straightforward than mixing JSON into query string. – Mike Brant Mar 05 '16 at 04:25
  • The `.success` and `.error` methods are deprecated and have been removed from the AngularJS framework. – georgeawg May 05 '19 at 05:09
6

You need to deserialize your form data before passing it as the second parameter to .post (). You can achieve this using jQuery's $.param (data) method. Then you will be able to on server side to reference it like $.POST ['email'];

bahramzy
  • 133
  • 3
  • 14
6

This is the best solution (IMO) as it requires no jQuery and no JSON decode:

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

Summary:

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

Example:

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

PHP code:

<?php
    $id = $_POST["id"];
?>
Community
  • 1
  • 1
lepe
  • 24,677
  • 9
  • 99
  • 108
  • What about: `JSON.stringify('{ id: 'some_id', name : 'some_name' }')` or `$httpParamSerializer` for client-side conversion? – droid192 Dec 30 '16 at 11:00
5

It's an old question but it worth to mention that in Angular 1.4 $httpParamSerializer is added and when using $http.post, if we use $httpParamSerializer(params) to pass the parameters, everything works like a regular post request and no JSON deserializing is needed on server side.

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

James Gentes
  • 7,528
  • 7
  • 44
  • 64
Dina
  • 937
  • 9
  • 12
1

It took me hours to understand that while working with Angular and PHP. Post data was not going to PHP in $_POST

in PHP code do the following. - Create a variable $angular_post_params - Then do the following $angular_http_params = (array)json_decode(trim(file_get_contents('php://input')));

now you can access your parameters like you do from $_POST

$angular_http_params["key"]

in case you were wondering about javascript....this is what i used

    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
  • 1,546
  • 17
  • 15
  • for newbies ... dont forget to have ng-app and ng-controller directives added to your content holder container on the page :) – Talha Jul 14 '16 at 08:06
  • The `.success` and `.error` methods are deprecated and have been removed from the AngularJS framework. – georgeawg May 05 '19 at 05:14