AngularJS - Any way for $http.post to send request parameters instead of JSON?
Asked Answered
N

13

117

I have some old code that is making an AJAX POST request through jQuery's post method and looks something like this:

$.post("/foo/bar", requestData,
    function(responseData)
    {
        //do stuff with response
    }

requestData is just a javascript object with some basic string properties.

I'm in the process of moving our stuff over to use Angular, and I want to replace this call with $http.post. I came up with the following:

$http.post("/foo/bar", requestData).success(
    function(responseData) {
        //do stuff with response
    }
});

When I did this, I got a 500 error response from the server. Using Firebug, I found that this sent the request body like this:

{"param1":"value1","param2":"value2","param3":"value3"}

The successful jQuery $.post sends the body like this:

param1=value1&param2=value2&param3=value3

The endpoint I am hitting is expecting request parameters and not JSON. So, my question is is there anyway to tell $http.post to send up the javascript object as request parameters instead of JSON? Yes, I know I could construct the string myself from the object, but I want to know if Angular provides anything for this out of the box.

Niobous answered 30/8, 2012 at 4:59 Comment(0)
G
140

I think the params config parameter won't work here since it adds the string to the url instead of the body but to add to what Infeligo suggested here is an example of the global override of a default transform (using jQuery param as an example to convert the data to param string).

Set up global transformRequest function:

var app = angular.module('myApp');

app.config(function ($httpProvider) {
    $httpProvider.defaults.transformRequest = function(data){
        if (data === undefined) {
            return data;
        }
        return $.param(data);
    }
});

That way all calls to $http.post will automatically transform the body to the same param format used by the jQuery $.post call.

Note you may also want to set the Content-Type header per call or globally like this:

$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';

Sample non-global transformRequest per call:

    var transform = function(data){
        return $.param(data);
    }

    $http.post("/foo/bar", requestData, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
        transformRequest: transform
    }).success(function(responseData) {
        //do stuff with response
    });
Gelinas answered 30/8, 2012 at 7:7 Comment(10)
I was wondering if there was something other than having a transformRequest function, but it sounds like there's not. Thanks for heads up about the jQuery param function.Niobous
Non-global per call method is working well for me, but when trying to set up globally via $httpProvider.defaults, then it not working, any clue about this ?Manhunt
WRT configuring it globally, I too am having issues. When I try to do it using the snippet given here, I get an error Cannot read property "jquery" of undefined. How do I fix this? PS. Per-call transformations work.Cricoid
@Cricoid What is happening is that the transformRequest function is getting called on a request with no data so 'data' is undefined. I added a guard prior to 'return $.param(data);'. Insert this as the first line to the transformRequest function: 'if (data === undefined) return data;' See the edit I made to the answer.Costume
In the local case, transformRequest erases the ones that Angular normally does right. In the non-global transformRequest how would one unshift or pull the function instead of over-writing?Helix
@Helix you can try something like this: transformRequest: [function(d) { return d; }, $http.defaults.transformRequest[0]] replacing that first function with your own. If you don't change it globally the $http.defaults.transformRequest will be a one item array with their default transformRequest handling. See this for reference.Gelinas
@Gelinas I found a another solution transformRequest: [function(){//my own function}].concat($http.defaults.transformRequest) by using concat you can run your own function before (or after) the default transformRequest. This also preserves any functions you may have previously added to $http.defaults.transformRequest. In your example you would just be using the first function in your default transformations.Helix
Not sure if it is just me, but I found by doing this $.param method, I am no longer able to use ":placeholder" in the URLs.Demean
Angular 1.4.0 can no longer modify request headers using transformRequest: https://mcmap.net/q/189250/-set-defaults-header-on-angularjs-but-don-39-t-use-it-on-one-specific-requestSkilling
as of Angular 1.4 you can use $httpParamSerializer instead of jQuery docs.angularjs.org/api/ng/service/$httpParamSerializerFrigate
U
22

If using Angular >= 1.4, here's the cleanest solution I've found that doesn't rely on anything custom or external:

angular.module('yourModule')
  .config(function ($httpProvider, $httpParamSerializerJQLikeProvider){
    $httpProvider.defaults.transformRequest.unshift($httpParamSerializerJQLikeProvider.$get());
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
});

And then you can do this anywhere in your app:

$http({
  method: 'POST',
  url: '/requesturl',
  data: {
    param1: 'value1',
    param2: 'value2'
  }
});

And it will correctly serialize the data as param1=value1&param2=value2 and send it to /requesturl with the application/x-www-form-urlencoded; charset=utf-8 Content-Type header as it's normally expected with POST requests on endpoints.

Ulna answered 18/5, 2016 at 9:50 Comment(0)
M
17

From AngularJS documentation:

params – {Object.} – Map of strings or objects which will be turned to ?key1=value1&key2=value2 after the url. If the value is not a string, it will be JSONified.

So, provide string as parameters. If you don't want that, then use transformations. Again, from the documentation:

To override these transformation locally, specify transform functions as transformRequest and/or transformResponse properties of the config object. To globally override the default transforms, override the $httpProvider.defaults.transformRequest and $httpProvider.defaults.transformResponse properties of the $httpProvider.

Refer to documentation for more details.

Mf answered 30/8, 2012 at 6:36 Comment(1)
I saw the params in the documentation, and like Gloopy mentions, I need it in the body, and not on the URL. I was wondering if there was some option or something I was missing to do the parameters instead of JSON, but it sounds like I just need to use the transformRequest property.Niobous
I
15

Use jQuery's $.param function to serialize the JSON data in requestData.

In short, using similar code as yours:

$http.post("/foo/bar",
$.param(requestData),
{
    headers:
    {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
}
).success(
    function(responseData) {
        //do stuff with response
    }
});

For using this, you have to include jQuery in your page along with AngularJS.

Imbricate answered 23/4, 2013 at 17:27 Comment(0)
G
7

Note that as of Angular 1.4, you can serialize the form data without using jQuery.

In the app.js:

module.run(function($http, $httpParamSerializerJQLike) {
  $http.defaults.transformRequest.unshift($httpParamSerializerJQLike);
});

Then in your controller:

$http({
    method: 'POST',
    url: myUrl',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    data: myData
});
Grammalogue answered 23/12, 2015 at 19:31 Comment(2)
This answer is great. It addresses the 2 main problems with Post from Angular. The header must be set correctly and you have to serialize the json data. If you don't need IE8 support use 1.4+ or later.Fiann
I just implemented this and it resolves issues I was having with post, but this also changes how patch works and appears to have broken all of my uses of $http.patch().Tigre
Y
5

I have problems as well with setting custom http authentication because $resource cache the request.

To make it work you have to overwrite the existing headers by doing this

var transformRequest = function(data, headersGetter){
  var headers = headersGetter();
  headers['Authorization'] = 'WSSE profile="UsernameToken"';
  headers['X-WSSE'] = 'UsernameToken ' + nonce
  headers['Content-Type'] = 'application/json';
};

return $resource(
  url,
    {
    },
    {
      query: {
        method: 'POST',
        url: apiURL + '/profile',
        transformRequest: transformRequest,
        params: {userId: '@userId'}
      },
    }
);

I hope I was able to help someone. It took me 3 days to figure this one out.

Ybarra answered 2/5, 2013 at 4:25 Comment(1)
I guess you just saved me 3 days of work. Thanks!!! I am still trying to figure out if I can intercept request call somehow so that I can inject a custom header for every call.Pocket
S
5

This might be a bit of a hack, but I avoided the issue and converted the json into PHP's POST array on the server side:

$_POST = json_decode(file_get_contents('php://input'), true);
Spear answered 13/11, 2013 at 13:41 Comment(2)
I've used this method, but I hate it; and took me a long time to figure out why I had to use this.Fredella
like I said - it feels hacky. Like most of php ;)Spear
A
4

Modify the default headers:

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

Then use JQuery's $.param method:

var payload = $.param({key: value});
$http.post(targetURL, payload);
Almita answered 30/11, 2014 at 19:58 Comment(0)
A
3
   .controller('pieChartController', ['$scope', '$http', '$httpParamSerializerJQLike', function($scope, $http, $httpParamSerializerJQLike) {
        var data = {
                TimeStamp : "2016-04-25 12:50:00"
        };
        $http({
            method: 'POST',
            url: 'serverutilizationreport',
            headers: {'Content-Type': 'application/x-www-form-urlencoded'},
            data: $httpParamSerializerJQLike(data),
        }).success(function () {});
    }
  ]);
Anthracosilicosis answered 4/5, 2016 at 10:1 Comment(1)
According to me its simplest and easiest...There may be many other waysAnthracosilicosis
C
2

Quick adjustment - for those of you having trouble with the global configuration of the transformRequest function, here's the snippet i'm using to get rid of the Cannot read property 'jquery' of undefined error:

$httpProvider.defaults.transformRequest = function(data) {
        return data != undefined ? $.param(data) : null;
    }
Cricoid answered 21/1, 2013 at 0:43 Comment(0)
R
1

You can also solve this problem without changing code in server, changing header in $http.post call and use $_POST the regular way. Explained here: http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/

Raeraeann answered 12/2, 2015 at 1:17 Comment(0)
O
0

I found many times problematic behavior of this whole. I used it from express (without typings) and the bodyParser (with the dt~body-parser typings).

I didn't try to upload a file, instead simply to interpret a JSON given in a post string.

The request.body was simply an empty json ({}).

After a lot of investigation finally this worked for me:

import { json } from 'body-parser';
...
app.use(json()); <-- should be defined before the first POST handler!

It may be also important to give the application/json content type in the request string from the client side.

Oysterman answered 2/9, 2016 at 14:39 Comment(1)
I am sorry for the "and sacrifice a black hen"-style answer, what is unfortunately common in the current stage of the typescript/node/angular environment.Oysterman
M
0

Syntax for AngularJS v1.4.8 + (v1.5.0)

       $http.post(url, data, config)
            .then(
                    function (response) {
                        // success callback
                    },
                    function (response) {
                        // failure callback
                    }
            );

Eg:

    var url = "http://example.com";

    var data = {
        "param1": "value1",
        "param2": "value2",
        "param3": "value3"
    };

    var config = {
        headers: {
            'Content-Type': "application/json"
        }
    };

    $http.post(url, data, config)
            .then(
                    function (response) {
                        // success callback
                    },
                    function (response) {
                        // failure callback
                    }
            );
Mcneal answered 28/6, 2018 at 18:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.