Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

AngularJS has different request data format from Jquery when send almost same post call #6039

Closed
wahyd4 opened this issue Jan 29, 2014 · 73 comments

Comments

@wahyd4
Copy link

wahyd4 commented Jan 29, 2014

I was tried to use AngularJS $http and coffeescript to send a cross domain post request. I've already changed the server side to make it available for cross domain request.
The question is when I was using Angular js to send request I found the request data is different from the request sent by jQuery.

Angular

login: (user, callback)=>
    baseUrl = 'http://localhost:3000/api/v1/sessions'
    @$http({
      method: 'POST',
      url: baseUrl,
      data: {
        "user":{
          "email":"wahxxx@gmail.com",
          "password":"123456"
        }
      },
      headers:
        'Content-Type': 'application/x-www-form-urlencoded'
    }).success (result)->
      callback(result)

then I got
qq20140129-2 2x

Jquery

$.ajax({
      type: "POST",
      url: "http://localhost:3000/api/v1/sessions",
      data: {
        "user":{
          "email":"wahxxx@gmail.com",
          "password":"123456"
        }
      },
      success: function(){

      }

Then it works well, this request data format is what I want.
qq20140129-1 2x

I don't know whether it's an issue, hope someone can help me out.
There is same question I posted on stackoverflow: http://stackoverflow.com/questions/21400743/angularjs-cant-send-post-request-with-content-typeapplication-json

@caitp
Copy link
Contributor

caitp commented Feb 4, 2014

You're asking for x-www-form-urlencoded, and angular doesn't encode data that way currently. I suppose we could, but I'm not sure I expect that to happen. I think for the time being you should just use jQuery when you need to send requests like this, and maybe if you want to implement this feature we could look at getting it into the tree. I'll mark this as PRz welcome.

@pkozlowski-opensource
Copy link
Member

Related to #1743. I agree with @caitp that we could easily make people's life a bit easier. I guess we could try to do is:

@linclark
Copy link
Contributor

Would it make sense for this functionality to live in a stand-alone module outside of core? Having it outside of core would make it easy to extend it and support other formats in the same way.

I put together a proof of concept, and a Plunker demo. If this seems like the right direction, I'd be happy to continue development on it.

@dcartertwo
Copy link

+1

@hsdk123
Copy link

hsdk123 commented May 25, 2014

From personal observation, all I need to do to get this working is to wrap the data property around with jQuery's $.param to get form posting working as it does normally.

Ex.

$http({
    url: ServerAddress+"/login", method: 'POST',
    data: $.param($scope.loginForm),
    headers : {'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}
})

I feel it would just make life easier if AngularJS could provide an equivalent function to jQuery's $.param - ideally something that can be used also with the transformRequest property.

@daghoidahl
Copy link

+1. Coming here from the thread at #1743, I agree that this is a feature that is missing from Angular. My team was actually quite surprised to find that there were hoops to jump through to make a regular form post. In our case we have jQuery as a dependency, so we can use that until Angular has a "native" solution.

@UzEE
Copy link

UzEE commented Jul 29, 2014

Facing the same issue as well. Recently started working on a HTML5 app (Cordova) which calls a PHP backend for HTTP requests. The PHP backend is powering a number of different projects so even simple calls like login and authentication expect form-data or application/x-www-form-urlencoded.

I've really liked the bits of AngularJS I've used but this came off as a very odd omission. Even if url-encoding isn't the standard when posting data, it is the convention expected by an overwhelming number of backend services, and Angular should support it out of the box.

@caitp
Copy link
Contributor

caitp commented Jul 29, 2014

expected by an overwhelming number of backend services

lets enumerate those services, particularly the ones where this is the only possible solution and it's simply impossible to do anything else

after we've done that and established how big of a problem this is, because most people don't seem to run into this issue.

@hsdk123
Copy link

hsdk123 commented Jul 29, 2014

lets enumerate those services, particularly the ones where this is the only possible solution and it's simply impossible to do anything else

There's almost always a workaround to things. I don't think you'll get a service where it's literally impossible to send forms with the current Angular implementation.

As I mentioned in my post, in the end, I only needed jQuery's $.param to change my data into something my service (golang backend) could digest.

However, it did take me an extremely long time before I managed to realise the reason why my forms weren't sending and to find this workaround I'm currently using. It would be great if Angular could provide an internal function similar to $.param, or at the very least mention this problem and possible solutions in the API reference page / wiki.

@iksose
Copy link

iksose commented Jul 29, 2014

A workaround for everything indeed, I never opened an issue because I assumed $.param was the defacto solution.

A native implementation would be great.

@paishin
Copy link

paishin commented Jul 29, 2014

I have the exact same problem, php server does not accept OPTION method natively so I guess the problem is quite big.

I have used $http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"; to switch requests to application/x-www-form-urlencoded to get the request through.

Unfortunately I do not have jQuery as a dependancy so $.param is not an option for me...are there any other workarounds?

@georgir
Copy link

georgir commented Aug 11, 2014

There must already be a function for this in angular, as it does it for 'params' key passed to $http... but it is not publicly accessible. I think this should change and be made public, and the default transformRequest should be changed too to serialize forms according the content-type header.

For now though, if you do not want to reimplement the wheel over and over (even if its just 5 lines, I do agree it is bad news having to write it yourself), a clever trick I saw recently was to pass FormData object as data, no-op transformRequest so it doesn't get stringified as [object FormData] (offtopic, html5 spec wtf, why no adequate toString?), and undefined as content-type header so XHR.send will set correct multipart headers. For older browsers, there are FormData polyfills. Note the request becomes multipart instead of urlencoded though, often that is ok but for some backends it may still not cut it.

@btford btford removed the gh: issue label Aug 20, 2014
@roymj88
Copy link

roymj88 commented Aug 28, 2014

+1 I too am facing the same issue. Hope this can be resolved from Angular itself?

@vqng
Copy link

vqng commented Aug 30, 2014

+1

@yorch
Copy link

yorch commented Sep 5, 2014

Same here.. I don't want to add jQuery as a dependency just for this

@ebuildy
Copy link

ebuildy commented Sep 7, 2014

Unbelievable, AngularJS is so awesome that I cannot believe this so simple thing is missing. So everyone is using jQuery only for this serialization function?

@iveoles
Copy link

iveoles commented Sep 9, 2014

+1 It's pretty crazy such an advanced framework doesn't have this by default

@gaboom
Copy link

gaboom commented Sep 16, 2014

+1 jQuery should not be required to fix this, pretty basic functionality, form submission...

@pihish
Copy link

pihish commented Sep 18, 2014

This should really be prioritized for addition into the next version

@shubhendusaurabh
Copy link

yes, lot of apis expect 'jquery' way so this functionality is required

@wcamarao
Copy link

+1 for angular to incorporate this

@rodrigograca31
Copy link

+1
Another user here waiting for this...
I should not need to include jquery just for this... This issue should be a priority!

@rodrigograca31
Copy link

For those trying to find a better solution:
http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/
(This works, and I don't need to use jquery!)

@frapontillo
Copy link
Contributor

👍

@cfontes
Copy link

cfontes commented Feb 25, 2015

Thanks @rodrigograca31.

This sucks. Come on guys, people are spending hours on this.

I bet most just go and add JQuery which is ridiculous, and in my case impossible...

@cfontes
Copy link

cfontes commented Feb 27, 2015

Just a heads up, this solution works great if you only need this to talk to Spring Security or something with simple requests
http://stackoverflow.com/a/14868725/564449

@georgir
Copy link

georgir commented Sep 8, 2015

@Narretz My previous response was directed specifically at @awebdeveloper and his code regarding older versions, sorry I didn't make it clear and may have left the impression I am criticizing angular's current version or something.

But to answer you anyway, I haven't used 1.4 but from looking at the code it seems ok. Basically exactly what I wanted in my first post on the issue - expose the functionality used by $http "params" config option to be more generally accessible. Now users can call it for their data.

Additional features like the default request transformer using that serializer for the request data based on the content-type header as proposed by others in this thread probably still fall under this issue.

[And regarding the paramSerializers specifically - a missing "JQLike" feature of the "JQLike" serializer is the ability to handle array of {name, value} objects, to support same key appearing multiple times in the data, i.e. for cases like a[]=1&a[]=2&a[]=3. Not a critical feature though, and not sure if this issue is the place to discuss that.]

@armanforghani
Copy link

/cc @Narretz
I am using AngularJS 1.4.6.
I have the following code in my module's config section but it does not work:

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

Also there is an unanswered SO question here.

@Narretz
Copy link
Contributor

Narretz commented Oct 23, 2015

@armanforghani I can't test right now, but this should work. Can you set up a plnkr that shows the problem?

@armanforghani
Copy link

@Narretz Please see here

@gkalpak
Copy link
Member

gkalpak commented Oct 27, 2015

@armanforghani, the paramSerializers are for serializing URL query params, not the payload/data of the request.
In your plnkr, there are no params to serialize.

@armanforghani
Copy link

How can i serialize data for post request? Currently i use this way. Can you recommend better option?

@gkalpak
Copy link
Member

gkalpak commented Oct 28, 2015

I can' really recommend a better option, because I never need to serialize the payload ala jQuery.
If I had to, I would try #6039 (comment) as my first option though.

@Narretz
Copy link
Contributor

Narretz commented Oct 28, 2015

@armanforghani There's also an example in the docs: https://docs.angularjs.org/api/ng/service/$httpParamSerializerJQLike Let me know if that doesn'T work

@lgalfaso
Copy link
Contributor

It should be possible to have this behavior by default for all post requests, you would need to do

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

@armanforghani
Copy link

@Narretz I'm looking for a way to set as default for all requests in module's config.
@lgalfaso Thank you. Interesting, i will try that.

@lgalfaso
Copy link
Contributor

@armanforghani please share if this actually worked, so other people can use the same mechanism if this is the behavior they are looking for

@armanforghani
Copy link

I just tested it and it works perfectly. @lgalfaso Thank you so much.

@lgalfaso
Copy link
Contributor

Given that there is a mechanism to achieve the desired behavior, then it should be safe to close this issue. If there is a spacial interest to make http request body data serialization configurable in an easier way, then open an issue linking this discussion.

@AdamMcCormick
Copy link

@lgalfaso I really disagree that this solves the problem. There is only a small subset of POST requests for which this is the right serializer so adding it for all requests isn't a good solution for the general case. The framework should be changed to use this serializer any time 'application/x-www-form-urlencoded' is the data type. Otherwise the framework is submitting bad data

@lgalfaso
Copy link
Contributor

lgalfaso commented Nov 2, 2015

@AdamMcCormick I think it is perfectly fine to keep on with the discussion on data serialization for POST requests. This includes what should be configurable, and if this configuration should be part of the core or a third-party module.
Now, there is no spec on how JSON data should be serialized. http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 This implies that there is no right or wrong way to serialize it. What you think is the right way will be someone else wrong way.

The way data is serialized is configurable as shown before, how this is done is documented https://docs.angularjs.org/api/ng/service/$http#transforming-requests-and-responses and there are some tools for different serialization mechanisms.
The current mechanism make it possible to create a transformation that works different when the data time is 'application/x-www-form-urlencoded' or something else, as you have access to the request header.

Changing the default behavior would be a breaking change and something that we try to avoid. This is why there are several configuration points that, hopefully, will help developers make things work the way they need with minimal code.

With all this information, it is not clear to me what should be changed. Can you please provide an example of something that is not possible, or that would need a substantial number of LOC to achieve?

@Narretz
Copy link
Contributor

Narretz commented Nov 2, 2015

@pkozlowski-opensource you had a plan for this, right? Maybe you can outline what you would have done, and me or someone else can take a shot at it?

@pkozlowski-opensource
Copy link
Member

@Narretz @lgalfaso what I was thinking of is to change the defaults so if someone sets 'application/x-www-form-urlencoded' as a header we would serialize differently (not using JSON but sth that "most people expect"). Obviously this would be a breaking change so can be only done in 1.5.x or 1.6.x

@AdamMcCormick
Copy link

@lgalfaso when I say I want "application/x-www-form-urlencoded" data (by telling angular the content type) it shouldn't be sending JSON at all! It should be sending the data object I provide as if it's a WWW Form (as the spec clearly outlines), like I tell it to. I'm not sure why that's so hard to understand. The current behavior is not spec compliant and thus wrong, so breaking change or not, it should change. It's not about what I think, and it doesn't matter if there's a workaround. @pkozlowski-opensource has stated several times how a solution could be implemented, and I am saying it should be.

@lgalfaso
Copy link
Contributor

lgalfaso commented Nov 4, 2015

@AdamMcCormick, would it be possible to know what standard are you talking about?
https://xhr.spec.whatwg.org/#the-send()-method is the standard for xhr that is what $http abstracts. Here, how the body should be serialized is only specified for two cases; an HTML document or a string.

@AdamMcCormick
Copy link

@lgalfaso you linked the spec for "application/x-www-form-urlencoded" above and it's pretty explicit as to how key/value pairs should be serialized and sent. When the content type is "application/x-www-form-urlencoded" the whole point is to emulate the way an HTML form would look if you had POST'd it. It's not a "body" it's a URL-encoded WWW form, it needs to act like it.

@lgalfaso
Copy link
Contributor

lgalfaso commented Nov 4, 2015

@AdamMcCormick the spec at http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 only applies to <form> and it would be reasonable to think that this should also apply to FormData objects, but there is nothing there that applies to JSON objects. At least none that I was able to find. If there is a spec that specifies this, please post it here as it would be a compelling reason to justify a breaking change. If not, then it is best to have a configurable option for the serializer, but keeping the current serializer as the default.

@AdamMcCormick
Copy link

@lgalfaso It's NOT A JSON OBJECT. It's a JavaScript object that I am trying to use to replace a FORM. "application/x-www-form-urlencoded" is the data format, not "application/json." You are conflating the two and that's why you don't seem to be hearing me. http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 specifies what ANY data transmitted with a content type of "application/x-www-form-urlencoded" MUST adhere to. That's "a compelling reason to justify a breaking change." What's more compelling than the current mode of operation being explicitly wrong?

@ribeirobreno
Copy link

TL, DR:
The docs should state the behavior for $http when serializing binary data, what is the default serialization and what serializations are available, if any beyond the default.
The best behavior in my opinion is that angular methods which result in a HTTP request should use a configuration OR the content-type header to decide which algorithm they will use for a serialization of binary data. In addition, if following the configuration route, Angular should set the correct content-type for the serialization chosen.

Now, there is no spec on how JSON data should be serialized.

JSON is the serialization, no need to serialize it again. 😁
It is specified in a RFC@IETF and at json.org.

application/x-www-form-urlencoded is another serialization specified as part of the HTML specs.

The parameter data from the $http function is expected receive any of two data types:
String: data in text format, it can be a text string or another data type serialized if needed
Object: a binary object which will be serialized either by Angular or the User Agent before sent via HTTP. We already know Angular does serialize it as JSON by default and ignores the content-type header, if set.

Why does this look like a bug?
$http.[post|get|put|...] documentation does not state the default serialization method used by Angular when the "data" parameter is a javascript object and does not behave like the most popular javascript frameworks (jQuery, Mootools, ...). Nor it behaves like what is the default expected by the most popular server-side technologies (PHP, Java, ASP.net, ...)

To add, the documentation also do not state that $http will ignore "Content-type" and any other parameter header to decide which serialization method it will use.

This is one use-case leading people to this github issue
By using a PHP server and Angular without jQuery (which is useless for a Angular app) for the first time (like myself), you will almost invariably try:
$http.post(url, jsObject).then(...)
This will fail, PHP by default deserializes x-www-form-urlencoded data into the $_POST variable, not json data.
Reviewing the Network inspector in firebug or chrome or dragonfly you may notice that the data has been serialized to JSON. No big deal:
Review your code, read the Angular docs. Find nothing wrong.
Expecting angular to serialize the binary data automatically into the most common serialization in HTTP, you will remember several other cases where you needed to explicitly set the data type as x-www-form-urlencoded, try to set the http header and hope the framework honors it:
$http.post(url, jsObject, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}}).then(...)

When this fails, you will think you or the framework did something wrong and will read the docs again, ask around, search the web, ...

Somewhere near this point, you will learn about $httpParamSerializer and maybe use it, go to the @angular issues page or both.

@doivosevic
Copy link

@ribeirobreno I agree completely. This has been my issue when working with angular 1 and after going throught exactly these steps and having to use $.param because I did not find out about $httpParamSerializer . After all this I came to the same problem with Angular 2 and now, again, my day is flying by while I'm trying to figure out how to do this without using jquery again since I have not been able to find $httpParamSerializer . What I'm interested even more is what backend framework are angular guys using not to come to this problem immediately since I've encountered this with both Flask and ASP.Net and on both the guys doing backend said it was my problem

@ribeirobreno
Copy link

@DominikDitoIvosevic i've looked into the docs today and found this: https://docs.angularjs.org/api/ng/service/$http#default-transformations

Don't know if i missed it before or if it was added... either way i think it would be really nice to have a reference to the "default transformations" next to the data parameters, here: https://docs.angularjs.org/api/ng/service/$http#usage

@tag
Copy link

tag commented Oct 14, 2016

I realize I'm coming at this very (very) late, but as someone new to Angular, I'm amazed that it's so awkward to get a www-form-urlencoded post body. I was very surprised that it wasn't the default, given (as @ribeirobreno said, masterfully) it's what's used everywhere else. I still don't understand why this difference is not called out explicitly in the documentation. Like so many others in this thread, I'm using PHP as a backend. It's easier for me to make the transformation server-side than via Angular, partly because even with the transforms in Angular, getting "normal" post bodies and content headers is still too much work in Angular. Whether I make the adjustment in Angular or in PHP, it's still one more thing I have to explain to my novice students.

This is one of several closed issues I've found on this topic. I won't comment on all of them, but I hope that enough voices will bring some visibility to the issue and make this quirk of Angular easier to use.

@hnsrikanth
Copy link

As a newbie to Angular ! It helped me in 2017 :)

@yeganehaym
Copy link

i faced to this problem in angular2
so i used this code here
http://stackoverflow.com/questions/35212341/angular2-http-post-request-parameters

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests