Consuming JSON with an array in ExpressJS - javascript

On the webpage I am POSTing some JSON using jQuery:
$.post('/url', data);
My data is a javascript object that contains some values and an array. JSON.stringify(data) looks like:
{"favoriteAnimal":"piglet", "okayAnimals":["cats","dogs"]}
I'm consuming this JSON in a NodeJS webapp using ExpressJS (which is wired up with the body-parser middleware). I can retrieve the favorite animal like req.body.favoriteAnimal and it gives me the string piglet which is all fine and dandy.
But how do I access the values in the array?
req.body.favoriteAnimal // piglet
req.body.okayAnimals // undefined
req.body.okayAnimals[] // syntax error
This works...
req.body['okayAnimals[]']
...but smells fishy. It also won't return an array if the original data that is being POSTed contains only one element in its array (it just returns a single string).
Is there something going on with the the jQuery encoding of the JSON or something going on with the decoding in ExpressJS that's preventing me from accessing it like req.body.okayAnimals and getting an array every time?

Kevin's answer almost got me there.
$.post('/url', JSON.stringify(data)) will send a string which is one step closer. Unfortunately jQuery's $.post sets the wrong header
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
which ExpressJS's body-parser will not handle appropriately. You end up with
req.body={"{\"favoriteAnimal\":\"piglet\",\"okayAnimals\":[\"cats\",\"dogs\"]}":""}
I rewrote how I was sending the data.
$.ajax({
url: '/url',
type: 'POST',
data: data,
contentType: 'application/json; charset=utf-8',
dataType: 'json'
})
I saw that my browser was sending the correct headers
Content-Type:application/json; charset=UTF-8
And observed
req.body={"favoriteAnimal":"piglet","okayAnimals":["cats","dogs"]}

JavaScript objects and JSON are two very differen things, which is the root of the problem you are seeing. What you are passing to $.post() is actually an object, not json, and is therefore being converted to a paramstring by jQuery before being sent to node. In this case, the paramstring is:
favoriteAnimal=piglet&okayAnimals[]=cats&okayAnimals[]=dogs
which of course explains why an okAnimals[] property exists on the body.
I'm surprised that the middleware you are using to parse the body isn't picking that up properly, but regardless you should instead pass a json string to $.post if you want to send it as is.
$.post('/url', JSON.stringify(data));

Related

OData returns array instead of JSON - How to convert?

I've been trying to figure how to properly receive an OData response in Javascript for a couple of days. The problem is the response is formatted as an array instead of JSON, so the function JSON.parse(mydata) does not work with the data I am receiving.
My question is two-fold: What is the proper way to request OData to send a response as JSON and/or how do I format my current response to be JSON?
Here is the code that I am using:
$.ajax({
type: "GET",
url: requestUri,
dataType: "script",
accept: "application/json",
success: function(data, request) {
var jsonData = JSON.parse(data);
},
error: function(msg) {
alert(msg);
}})
Here is an example response of logging the variable data with console.log:
{"#odata.context":"http://localhost:5001/odata/$metadata#Movies","value":[{"Title":"Pulp Fiction","Genre":"Action","RunTime":0,"Sales":0,"Key":2}]}
The problem is the response is formatted as an array instead of JSON
It can't be. You can't send "an array" over HTTP. You have to encode it somehow … e.g. as JSON.
jQuery will, unless you override it with the dataType option, use the Content-Type HTTP response header to determine how the data is encoded. If it is JSON, it will parse the JSON.
The value of data is what you would get if you read the raw responseText and passed it through JSON.parse.
So just don't try to parse it manually. jQuery has done it for you.
Skip the step:
var jsonData = JSON.parse(data);
… and just work with data.
NB: The output of JSON.parse is an object, array or other JavaScript data type. JSON data is what you get from JSON.stringify.
If the response is in OData format, as your example shows, but you want it in JSON format that can be parsed by JSON.parse, add $format=json to the OData query, so long as the OData endpoint supports it. I know Dynamics 365 does not.
You can add it to a variable and access it just like that.
var v={"#odata.context":"http://localhost:5001/odata/$metadata#Movies","value":[{"Title":"Pulp Fiction","Genre":"Action","RunTime":0,"Sales":0,"Key":2}]};
//v.value[0] is a json object
console.log(v.value[0]);
or skip the assignment altogether and access this way:
data.value[0]
data.value[0].Genre
data.value[0].RunTime
etc....

How to make such a structure in javascript

I'm new to JS. And have a basic silly doubt. Please bear with me.I want to send a request of the form:
{"user":{"username":"myuser","password":"mypass","role":"myrole"}, "organization":"org_name"}
such that the user object can be access by req.body.user and organisation can be accessed by req.body.organization.
But when I'm sending this request:
it translates to-
{
"user[username]": "myuser",
"user[password]": "mypass",
"user[role]": "myrole",
"organization": "org_name"
}
When I just send
{"user":{"username":"myuser","password":"mypass","role":"myrole"}}
then I can access using req.body.user, but not the way mentioned above. Why is this so?
How can I send the request now such that I can access the request.body.user and req.body.organization properly?
EDIT:
This is how the request is sent. Front end: ember, backend node/express:
Ember.$.post("http://"+window.location.hostname+":3000/api/organizations/customer",{"user":{"username":"myuser","password":"mypass","role":"myrole"}, "organization":"org_name"},function(data){
console.log(JSON.stringify(data),null, " ");
});
Receiving side:
console.log(JSON.stringify(req.body,null," "));
I am trying to create the user but req.body.user is undefined. Though I can use user[username] and proceed, but that s now how I want to do
You aren't sending JSON to the server. Passing an object in as data doesn't send it as an object; it simply gets converted to a query string [source]. If you want to send JSON, you need to use JSON.stringify on the data that you send, not the data you receive.
Ember.$.post("http://"+window.location.hostname+":3000/api/organizations/customer",JSON.stringify({"user":{"username":"myuser","password":"mypass","role":"myrole"}, "organization":"org_name"}),function(data){
console.log(data);
});
With the help of Christian Varga's answer to this post, I could further dive in. Though the problem was still not solved, but it gave some insight of how can that be solved.
This is what I did next for such a structure. As per his suggestion, I used JSON.stringify but at a different place:
Ember.$.post("http://"+window.location.hostname+":3000/api/organizations/customer",{"user":JSON.stringify({"username":"myuser","password":"mypass","role":"myrole"}), "organization":"org_name"},function(data){
console.log(data);
});
On the backend server side I could then receive req.body.user. But the req.body.user was stringified JSON. To further access this inner JSON, I had to use JSON.parse.
Though this worked, but I was looking for more better solution, since this backend change worked for my front end implementation, but was failing for RESTClient.
I understood I need to pass the contentType of the data to be sent in the post request. I could not find $.post argument for contentType. So then I used $.ajax call.
Ember.$.ajax({
url: "http://"+window.location.hostname+":3000/api/organizations/customer",
type:"POST",
data: JSON.stringify({user:{username:username,password:password,role:role}, organization_id:organization_id}),
contentType: "application/json; charset=utf-8",
dataType:"json",
success: function(data){
console.log(JSON.stringify(data),null, " ");
}
});
where the username, password, role, organization_id are already assigned variables.

Nodejs json string shows as [object Object]

Front End
// js / jquery
var content = {
info : 'this is info',
extra : 'more info'
}
$.ajax({
type: 'POST',
url: '/tosave',
data: content
});
Node
// app.js
app.post('/tosave', function(req, res){
fs.writeFile('/saved.txt', req.body, function(err, data){})
});
This saves to the file as [object Object]. I get the same result when I use JSON.stringify() on content before sending it through the ajax request. However, if I stringify it in node.js it successfully saves as {"info":"this is the info","extra":"this is extra"}.
My question is why this happens? Also, how would I go about converting it to a json string before the ajax request?
If you really want to send text (e.g. that happens to look like correct JSON, but isn't), and don't want Node to see it as JSON and convert it into an object for you, you could try specifying:
$.ajax({
type: 'POST',
url: '/tosave',
data: content,
processData: false
});
This will cause the data to be sent with application/x-www-form-urlencoded which might possibly prevent Node from helpfully turning the data into a JSON object.
However I would recommend simply stringifying req.body before writing it to the file. writeFile doesn't do this for you because you can send a buffer instead of a string, I suppose.
It seems like both jquery and node are being very helpful here.
Jquery recognises that you are sending an object and automatically converts into a format that can be transported before it sends it.
Node recognises what you are sending and decodes it when it receives it, turning it back into an object.
Basically this means that you don't have to worry about the transport.
What's not to like about that?

what is wrong with my jQuery POST request?

I am trying to authenticate with a remote service through jQuery. First, I verify that I can do this outside of the browser:
curl -X POST -H "Content-Type: application/json" -H "Accept: appliction/json" -d '{"username":"...","password":"..."}' http://example.com/auth
This successfully returns a token.
Now I try it with jQuery:
$.ajax({
url: "http://example.com/auth",
type: "POST",
dataType: "json",
contentType: "json",
data: {username:"...",password:"..."},
error: function(data){alert(JSON.stringify(data))},
success: function(data){alert(JSON.stringify(data))}
});
I get a server error (500). Clearly I am doing something wrong. This is my first time trying to do a POST in jQuery, so I don't know how to identify what the problem is. What am I doing wrong here?
P.S. I can successfully do a GET request through jQuery, if I already have a token:
$.ajax({
url: "http://example.com/stuff?token=f42652adcbfe3ed9d59fae62b5267b8d",
type: "GET",
dataType: "json",
error: function(data){alert(JSON.stringify(data))},
success: function(data){alert(JSON.stringify(data))}
});
The only thing I notice is a difference in the data representations. Look at the data in the original request:
-d '{"username":"...","password":"..."}'
And the data in the AJAX request:
data: {username:"...",password:"..."}
The former wraps the keys in strings, whereas the latter doesn't. The whole thing should be a string too. Try:
data: '{"username":"...","password":"..."}'
This would be more consistent with JSON formatted data in general, I believe. Without wrapping the keys in strings, it may be a JavaScript object but it's not JSON data.
Update: oops missed a comment saying stringify didn't work. I'll leave this for posterity
Sometimes you need to stringify the data when sending Json, otherwise jquery may serialize the object as a param string rather than as a whole object. It depends on how your server binds the request query to the object. though. Can you debug the server request or is that out of your hands?
Try doing (provided you are on a semi modern browser) :
data: JSON.stringify({ your: data})
Thank you everybody for your help. Each little piece of advice helped lead me to the solution. I said it in a comment already, but here is the solution:
The dataType is correctly listed as "json", but the contentType should be listed as "application/json".
The content must be wrapped in JSON.stringify.
Do you post to the same domain as the js was loaded from? If not you need to use jsonp and make sure the server explicitly accepts your request, I believe.
500 Internal Server Error
The server encountered an unexpected condition which prevented it from fulfilling the request.
Json response from the url may be the reason, you can comment the stringfy function and alert the response. You can use try/catch method in response and check the error.
1) Open Network in Tools (f12)
2) Select "Network"
3) Select error row
4) Open "Body" on rights side
5) In title you can see error description eq.
<title>The parameters dictionary contains a null entry for parameter 'Id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Delete(Int32)' in 'BosCatalog.Controllers.ProductsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.<br>Parameter name: parameters</title>

How to send data in request body with a GET when using jQuery $.ajax()

The service API I am consuming has a given GET method that requires the data be sent in the body of the request.
The data required in the body is a list of id's separated by hypen and could potentially be very large and thus it must be sent in the body otherwise it will likely foobar somewhere in the browsers/proxies/webservers etc chain. Note I don't have control over the service or API so please don't make suggestions to change it.
I am using the following jQuery code however observing the request/response in fiddler I can see that the "data" I am sending is ALWAYS converted and appended to the query string despite me setting the "processData" option to false...
$.ajax({
url: "htttp://api.com/entity/list($body)",
type: "GET",
data: "id1-id2-id3",
contentType: "text/plain",
dataType: "json",
processData: false, // avoid the data being parsed to query string params
success: onSuccess,
error: onError
});
Anyone know how I can force the "data" value to be sent in the body of the request?
In general, that's not how systems use GET requests. So, it will be hard to get your libraries to play along. In fact, the spec says that "If the request method is a case-sensitive match for GET or HEAD act as if data is null." So, I think you are out of luck unless the browser you are using doesn't respect that part of the spec.
You can probably setup an endpoint on your own server for a POST ajax request, then redirect that in your server code to a GET request with a body.
If you aren't absolutely tied to GET requests with the body being the data, you have two options.
POST with data: This is probably what you want. If you are passing data along, that probably means you are modifying some model or performing some action on the server. These types of actions are typically done with POST requests.
GET with query string data: You can convert your data to query string parameters and pass them along to the server that way.
url: 'somesite.com/models/thing?ids=1,2,3'
we all know generally that for sending the data according to the http standards we generally use POST request.
But if you really want to use Get for sending the data in your scenario
I would suggest you to use the query-string or query-parameters.
1.GET use of Query string as.
{{url}}admin/recordings/some_id
here the some_id is mendatory parameter to send and can be used and req.params.some_id at server side.
2.GET use of query string as{{url}}admin/recordings?durationExact=34&isFavourite=true
here the durationExact ,isFavourite is optional strings to send and can be used and req.query.durationExact and req.query.isFavourite at server side.
3.GET Sending arrays
{{url}}admin/recordings/sessions/?os["Windows","Linux","Macintosh"]
and you can access those array values at server side like this
let osValues = JSON.parse(req.query.os);
if(osValues.length > 0)
{
for (let i=0; i<osValues.length; i++)
{
console.log(osValues[i])
//do whatever you want to do here
}
}
Just in case somebody ist still coming along this question:
There is a body query object in any request. You do not need to parse it yourself.
E.g. if you want to send an accessToken from a client with GET, you could do it like this:
const request = require('superagent');
request.get(`http://localhost:3000/download?accessToken=${accessToken}`).end((err, res) => {
if (err) throw new Error(err);
console.log(res);
});
The server request object then looks like {request: { ... query: { accessToken: abcfed } ... } }
You know, I have a not so standard way around this. I typically use nextjs. I like to make things restful if at all possible. If I need to make a get request I instead use post and in the body I add a submethod parameter which is GET. At which point my server side handles it. I know it's still a post method technically but this makes the intention clear and I don't need to add any query parameters. Then the get method handles a get request using the data provided in the post method. Hopefully this helps. It's a bit of a side step around proper protocol but it does mean there's no crazy work around and the code on the server side can handle it without any problems. The first thing present in the server side is if(subMethod === "GET"){|DO WHATEVER YOU NEED|}

Categories

Resources