AngularJs $http.post() does not send data

2018-12-31 05:29发布

Could anyone tell me why the following statement does not send the post data to the designated url? The url is called but on the server when I print $_POST - I get an empty array. If I print message in the console before adding it to the data - it shows the correct content.

$http.post('request-url',  { 'message' : message });

I've also tried it with the data as string (with the same outcome):

$http.post('request-url',  "message=" + message);

It seem to be working when I use it in the following format:

$http({
    method: 'POST',
    url: 'request-url',
    data: "message=" + message,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});

but is there a way of doing it with the $http.post() - and do I always have to include the header in order for it to work? I believe that the above content type is specifying format of the sent data, but can I send it as javascript object?

30条回答
孤独寂梦人
2楼-- · 2018-12-31 05:45

I've been using the accepted answer's code (Felipe's code) for a while and it's been working great (thanks, Felipe!).

However, recently I discovered that it has issues with empty objects or arrays. For example, when submitting this object:

{
    A: 1,
    B: {
        a: [ ],
    },
    C: [ ],
    D: "2"
}

PHP doesn't seem to see B and C at all. It gets this:

[
    "A" => "1",
    "B" => "2"
]

A look at the actual request in Chrome shows this:

A: 1
:
D: 2

I wrote an alternative code snippet. It seems to work well with my use-cases but I haven't tested it extensively so use with caution.

I used TypeScript because I like strong typing but it would be easy to convert to pure JS:

angular.module("MyModule").config([ "$httpProvider", function($httpProvider: ng.IHttpProvider) {
    // Use x-www-form-urlencoded Content-Type
    $httpProvider.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8";

    function phpize(obj: Object | any[], depth: number = 1): string[] {
        var arr: string[] = [ ];
        angular.forEach(obj, (value: any, key: string) => {
            if (angular.isObject(value) || angular.isArray(value)) {
                var arrInner: string[] = phpize(value, depth + 1);
                var tmpKey: string;
                var encodedKey = encodeURIComponent(key);
                if (depth == 1) tmpKey = encodedKey;
                else tmpKey = `[${encodedKey}]`;
                if (arrInner.length == 0) {
                    arr.push(`${tmpKey}=`);
                }
                else {
                    arr = arr.concat(arrInner.map(inner => `${tmpKey}${inner}`));
                }
            }
            else {
                var encodedKey = encodeURIComponent(key);
                var encodedValue;
                if (angular.isUndefined(value) || value === null) encodedValue = "";
                else encodedValue = encodeURIComponent(value);

                if (depth == 1) {
                    arr.push(`${encodedKey}=${encodedValue}`);
                }
                else {
                    arr.push(`[${encodedKey}]=${encodedValue}`);
                }
            }
        });
        return arr;
    }

    // Override $http service's default transformRequest
    (<any>$httpProvider.defaults).transformRequest = [ function(data: any) {
        if (!angular.isObject(data) || data.toString() == "[object File]") return data;
        return phpize(data).join("&");
    } ];
} ]);

It's less efficient than Felipe's code but I don't think it matters much since it should be immediate compared to the overall overhead of the HTTP request itself.

Now PHP shows:

[
    "A" => "1",
    "B" => [
        "a" => ""
    ],
    "C" => "",
    "D" => "2"
]

As far as I know it's not possible to get PHP to recognize that B.a and C are empty arrays, but at least the keys appear, which is important when there's code that relies on the a certain structure even when its essentially empty inside.

Also note that it converts undefineds and nulls to empty strings.

查看更多
宁负流年不负卿
3楼-- · 2018-12-31 05:47

I use jQuery param with AngularJS post requrest. Here is a example ... create AngularJS application module, where myapp is defined with ng-app in your HTML code.

var app = angular.module('myapp', []);

Now let us create a Login controller and POST email and password.

app.controller('LoginController', ['$scope', '$http', function ($scope, $http) {
    // default post header
    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
    // send login data
    $http({
        method: 'POST',
        url: 'https://example.com/user/login',
        data: $.param({
            email: $scope.email,
            password: $scope.password
        }),
        headers: {'Content-Type': 'application/x-www-form-urlencoded'}
    }).success(function (data, status, headers, config) {
        // handle success things
    }).error(function (data, status, headers, config) {
        // handle error things
    });
}]);

I don't like to exaplain the code, it is simple enough to understand :) Note that param is from jQuery, so you must install both jQuery and AngularJS to make it working. Here is a screenshot.

enter image description here

Hope this is helpful. Thanks!

查看更多
笑指拈花
4楼-- · 2018-12-31 05:47

Add this in your js file:

$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";

and add this to your server file:

$params = json_decode(file_get_contents('php://input'), true);

That should work.

查看更多
后来的你喜欢了谁
5楼-- · 2018-12-31 05:48

This has finally been addressed in angular 1.4 using $httpParamSerializerJQLike

See https://github.com/angular/angular.js/issues/6039

.controller('myCtrl', function($http, $httpParamSerializerJQLike) {
$http({
  method: 'POST',
  url: baseUrl,
  data: $httpParamSerializerJQLike({
    "user":{
      "email":"wahxxx@gmail.com",
      "password":"123456"
    }
  }),
  headers:
    'Content-Type': 'application/x-www-form-urlencoded'
})})
查看更多
人间绝色
6楼-- · 2018-12-31 05:49

It's not angular's fault. Angular is designed to work in JSON world. So when $http service send AJAX request, it send all your data as a payload, not as form-data so that your backend application can handle it. But jQuery does some things internally. You instruct jQuery's $ajax module to bind form-data as JSON but before sending AJAX request, it serialized JSON and add application/x-www-form-urlencoded header. This way your backend application able to received form-data in form of post parameters and not JSON.

But you can modify angular $http service's default behavior by

  1. Adding header
  2. Serializing json

$httpParamSerializerJQLike is angular's in-built service which serializes json in the same way $.param does of jQuery.

$http({
    method: 'POST',
    url: 'request-url',
    data: $httpParamSerializerJQLike(json-form-data),
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8;'
    }
});

If you need a plugin to serialize form-data into JSON first, use this one https://github.com/marioizquierdo/jquery.serializeJSON

查看更多
流年柔荑漫光年
7楼-- · 2018-12-31 05:50

Unlike JQuery and for the sake of pedantry, Angular uses JSON format for POST data transfer from a client to the server (JQuery applies x-www-form-urlencoded presumably, although JQuery and Angular uses JSON for data imput). Therefore there are two parts of problem: in js client part and in your server part. So you need:

  1. put js Angular client part like this:

    $http({
    method: 'POST',
    url: 'request-url',
    data: {'message': 'Hello world'}
    });
    

AND

  1. write in your server part to receive data from a client (if it is php).

            $data               = file_get_contents("php://input");
            $dataJsonDecode     = json_decode($data);
            $message            = $dataJsonDecode->message;
            echo $message;     //'Hello world'
    

Note: $_POST will not work!

The solution works for me fine, hopefully, and for you.

查看更多
登录 后发表回答