How can I access my req.body data/json in Express? - javascript

I am new to node.js and I am trying to access json on my node.js server from a post request so I can send it to an API and feed it back to my front-end js file. I can see the json object, but I can't seem to access it(ex: req.body.name) after reading some documentation/stackoverflow posts.
Here is my post route from my server.js file, and packages:
var prettyjson = require('prettyjson');
var express = require('express');
var http = require('http');
var cors = require('cors');
var bodyParser = require('body-parser');
var app = express();
// create application/json parser
app.use(bodyParser.json());
// create application/x-www-form-urlencoded parser
app.use(bodyParser.urlencoded({
extended: true
}));
app.post('/', function(req, res) {
var test = req.body; //If I req.body.name here, it will return undefined
console.log(test);
});
Here is my front end map.js file post function and data:
var locations = [
{name:'Le Thai', coords:{lat:36.168743, lng:-115.139866}},
{name:'Atomic Liquors', coords:{lat:36.166782, lng:-115.13551}},
{name:'The Griffin', coords:{lat:36.168785, lng:-115.140329}},
{name:'Pizza Rock', coords:{lat:36.17182, lng:-115.142304}},
{name:'Mob Museum', coords:{lat:36.172815,lng:-115.141242}},
{name:'Joe Vicari’s Andiamo Italian Steakhouse', coords:{lat:36.169437, lng:-115.142903}},
{name:'eat', coords:{lat:36.166535, lng:-115.139067}},
{name:'Hugo’s Cellar', coords:{lat:36.169915, lng:-115.143861}},
{name:'Therapy', coords:{lat:36.169041, lng:-115.139829}},
{name:'Vegenation', coords:{lat:36.167401, lng:-115.139453}}
];
//convert array to JSON
var jsonStr = JSON.stringify(locations);
$.post('http://localhost:3000/', jsonStr, function(data){
//empty for now
},'json');
End goal: I want to be able to access my data like req.body.name. I tried using typeof on req.body, and it returns an object, however I can't seem to access this object. And I tried using JSON.parse, but realized req.body is already an object. I would like to serve this data to the Yelp API eventually.
Current output(per request) from console.log(req.body):
{ '{"name":"Le Thai","coords":{"lat":36.168743,"lng":-115.139866}},
{"name":"Atomic Liquors","coords":{"lat":36.166782,"lng":-115.13551}},
{"name":"The Griffin","coords":{"lat":36.168785,"lng":-115.140329}},
{"name":"Pizza Rock","coords":{"lat":36.17182,"lng":-115.142304}},
{"name":"Mob Museum","coords":{"lat":36.172815,"lng":-115.141242}},
{"name":"Joe Vicari’s Andiamo Italian Steakhouse","coords":
{"lat":36.169437,"lng":-115.142903}},{"name":"eat","coords":
{"lat":36.166535,"lng":-115.139067}},{"name":"Hugo’s Cellar","coords":
{"lat":36.169915,"lng":-115.143861}},{"name":"Therapy","coords":
{"lat":36.169041,"lng":-115.139829}},{"name":"Vegenation","coords":
{"lat":36.167401,"lng":-115.139453}}': '' }

You're using an array, so it will not be:
req.body.name
but e.g.
req.body[0].name
You probably want to iterate over the array that you get with .forEach or a for loop etc.

The problem is you're not telling the server you're sending it JSON, so it's not getting parsed. Also, as rsp pointed out, to access the first name, you'd want req.body[0].name, not req.body.name.
The dataType parameter on $.post isn't to tell the server what you're sending it, it's to tell jQuery what you're expecting back from the server. To tell the server what you're sending it, use $.ajax and the contentType option:
$.ajax({
url: 'http://localhost:3000/',
type: "POST",
contentType: "application/json", // <====
data: jsonStr,
success: function(data){
//empty for now
}
});
Now, the body-parser module sees the content type on the request, and parses it for you. So for instance, if I change your server file to do this:
app.post('/', function(req, res) {
req.body.forEach(function(entry, index) {
console.log(index, entry.name)
});
});
...then with the change above to the client code, I get this on the server console:
0 'Le Thai'
1 'Atomic Liquors'
2 'The Griffin'
3 'Pizza Rock'
4 'Mob Museum'
5 'Joe Vicari’s Andiamo Italian Steakhouse'
6 'eat'
7 'Hugo’s Cellar'
8 'Therapy'
9 'Vegenation'

For those getting an empty object in req.body
I had forgotten to set headers: {"Content-Type": "application/json"} in the request. Changing it solved the problem

Related

Seem to Have the Wrong Content Type When POSTing with Chai-HTTP

I am looking to make use of Chai-HTTP for some testing. Naturally I want to test more than my GETs however I seem to be hitting a major roadblock when attempting to make POSTs.
In an attempt to figure out why my POSTs weren't working I began hitting them against a POST test server.
Here is a POST attempt formatted using an entirely different toolchain (Jasmine-Node and Frisby) for testing (that works just fine):
frisby.create('LOGIN')
.post('http://posttestserver.com/post.php', {
grant_type:'password',
username:'helllo#world.com',
password:'password'
})
.addHeader("Token", "text/plain")
.expectStatus(200)
})
.toss();
Which results in:
Time: Mon, 27 Jun 16 13:40:54 -0700
Source ip: 204.191.154.66
Headers (Some may be inserted by server)
REQUEST_URI = /post.php
QUERY_STRING =
REQUEST_METHOD = POST
GATEWAY_INTERFACE = CGI/1.1
REMOTE_PORT = 19216
REMOTE_ADDR = 204.191.154.66
HTTP_CONNECTION = close
CONTENT_LENGTH = 64
HTTP_HOST = posttestserver.com
HTTP_TOKEN = text/plain
CONTENT_TYPE = application/x-www-form-urlencoded
UNIQUE_ID = V3GPVkBaMGUAAB1Uf04AAAAc
REQUEST_TIME_FLOAT = 1467060054.9575
REQUEST_TIME = 1467060054
Post Params:
key: 'grant_type' value: 'password'
key: 'username' value: 'hello#world.com'
key: 'password' value: 'password'
Empty post body.
Upload contains PUT data:
grant_type=password&username=hello%40world.com&password=password
And here is a POST attempt using Chai and Chai-HTTP. I would expect this to work the same as the above example using Jasmine and Frisby, however, you'll see the actual request differs in several ways.
describe('/post.php', function() {
var endPointUnderTest = '/post.php';
it('should return an auth token', function(done) {
chai.request('http://posttestserver.com')
.post(endPointUnderTest)
.set('Token', 'text/plain')
.send({
grant_type: 'password',
username: 'hello#world.com',
password: 'password'
})
.end(function(err, res) {
console.log(res);
res.should.have.status(200);
done();
});
});
});
Which results in:
Time: Tue, 28 Jun 16 06:55:50 -0700
Source ip: 204.191.154.66
Headers (Some may be inserted by server)
REQUEST_URI = /post.php
QUERY_STRING =
REQUEST_METHOD = POST
GATEWAY_INTERFACE = CGI/1.1
REMOTE_PORT = 1409
REMOTE_ADDR = 204.191.154.66
HTTP_CONNECTION = close
CONTENT_LENGTH = 76
CONTENT_TYPE = application/json
HTTP_TOKEN = text/plain
HTTP_USER_AGENT = node-superagent/2.0.0
HTTP_ACCEPT_ENCODING = gzip, deflate
HTTP_HOST = posttestserver.com
UNIQUE_ID = V3KB5kBaMGUAAErPF6IAAAAF
REQUEST_TIME_FLOAT = 1467122150.9125
REQUEST_TIME = 1467122150
No Post Params.
== Begin post body ==
{"grant_type":"password","username":"hello#world.com","password":"password"}
== End post body ==
Upload contains PUT data:
{"grant_type":"password","username":"hello#world.com","password":"password"}
Notice the difference in CONTENT_TYPE, Post Params and PUT data in particular (I think this is the source of my problem).
Where Jasmine/Frisby would submit the POST using the 'application/x-www-form-urlencoded' format, Chai-HTTP seems to be using the 'application/json' format.
Am I somehow misusing Chai-HTTP's POST capabilities? Or does Chai-HTTP not allow for 'application/x-www-form-urlencoded' POST requests? I do not seem to be able to resolve this and it is the final hurdle for me to jump to make the transition to using a Mocha/Chai toolchain for my testing (which is the goal, I would prefer to not use a different library unless it's absolutely necessary).
Having discussed this further on Chai-HTTP's Git-Hub page, I was able to find out that this is expected behaviour of SuperAgent, the HTTP request library under the hood of Chai-HTTP, which auto-detects the content-type based on what kind of data is contained in the .send() call.
I stumbled across this particular question as well which helped clarify what the difference between content-types actually was.
If anyone else runs into this problem, I've learned that Chai-HTTP's POST requests can be altered quite easily (kudos to meeber's help here) using calls like this:
//Override auto-detection by specifying the header explicitly
.set('content-type', 'application/x-www-form-urlencoded')
//Select the type 'form'
.type('form')
//Pass multiple strings in send instead of using an object
.send('grant_type=password')
.send('username=hello#world.com')
.send('password=password')
Creating a request that looks like this:
describe('/post.php', function() {
var endPointUnderTest = '/post.php';
it('should return an auth token', function(done) {
chai.request('http://posttestserver.com')
.post(endPointUnderTest)
.set('Token', 'text/plain')
.set('content-type', 'application/x-www-form-urlencoded')
.type('form')
.send('grant_type=password')
.send('username=hello#world.com')
.send('password=password')
.end(function(err, res) {
console.log(res);
res.should.have.status(200);
done();
});
});
});

NodeJS: Accessing object fields returns undefined even though fields exist

router.get('/:id', function(req, res, next){console.log(req.params.id)
request(
config.API_URL + "/v1/gallery/get?id=" + req.params.id,
function (err, response, body){
console.log('###BODY###',JSON.stringify(body));
console.log('###BODY###',JSON.stringify(body.data));
res.render('gallery', { user: req.session.user, gallery: body.data, title: 'Gallery', purchased: req.session.user.outlet ? (req.session.user.outlet.purchased || []) : [], config: config });
}
);
});
I'm trying to pass the request body's data field as the gallery for this template, but upon passing body.data, in the template it says my gallery argument is undefined. As you can see above, I then console logged the body and then its field. console.log(body) yields the following output:
###BODY### "{\"err\":null,\"data\": {\"_id\":\"5d955d7431d34f862a0dbd60\",\"owner
\":null,\"caption\":\"A suspected shooting at the Washington DC Navy Yard has sh
ut down parts of the city. This is the same location where a gunman killed 12 pe
ople in 2013. After an investigation search, authorities gave the \\\"all clear.
\\\"\",\"tags\":[\"dc\",\"navyyard\",\"shooting\",\"washington\"]...
I shortened the output, but as you can see, the data field is clearly there next to data.err. However, when I run console.log('###BODY###',JSON.stringify(body.data)), I am returned ###BODY### undefined. Can anyone explain this behavior please?
Replace this:
request(
config.API_URL + "/v1/gallery/get?id=" + req.params.id,
<callback>
);
With:
request({
url: config.API_URL + "/v1/gallery/get?id=" + req.params.id,
json: true
}, <callback>);
That will instruct request to automatically parse the response body as json (assuming you're using this request module, of course).

How to get POSTed (jquery) array data in Node.js (using express)

I am trying to post an array to my server. But I have difficulties in doing it properly.
The array I am trying to post, is an array of objects which is dynamically structured, thus I don't know it's length.
To be more precise, my array is of the form.
var names =[{id:1, name:"nick"},{id:2,name:"bob"},{id:3,name:"john"}.....{id:n, name:"whatever"}]
I am posting using jquery:
$.post("save_names", {
'names[]': names
}, function(results) {
alert(results);
});
My node code, is the following: (I use stormpath-express)
app.post('/save_names', config.access_group, function(req, res) {
console.log("body ", req.body);
});
This way i am getting the following from the console.log
body { 'names[]': [ '[object Object]', '[object Object]', '[object Object]' ] }
When i try to print the array : console.log("body ", req.body.names);
I get body undefined
Can somebody explain why this is happening? How to solve my error, and why can't I just post names:names and simply work?
You're sending your data incorrectly. You can examine request in Development tools. You'll see something like this:
Form Data
names[]:[object Object]
names[]:[object Object]
names[]:[object Object]
names[]:[object Object]
Try converting data to JSON yourself:
$.post("save_names", {
'names[]': JSON.stringify(names)
}, function(results) {
alert(results);
});
Don't forget to correctly access your array: console.log("body ", req.body['names[]']);.
Yes, you req.body contains key names[], not names. So you can either grab from req.body['names[]'] or rewrite code to have name object:
$.post("save_names", {
names: names
}, function(results) {
alert(results);
});
And express code:
app.post('/alter_offer_sort', config.access_group, function(req, res) {
console.log("body ", req.body.names);
});
P.S. probably you grab [] names from a GET Query. It's not how POST works.
UPDATE:
I also don't notice, that there is just string of object, so initialize bodyParser.
First install body-parser:
npm install --save body-parser
Then modify code to this:
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())

Specifying content-type in Meteor (JavaScript)

How can I specify content-type in Meteor?
I've got a page that returns JSON but response header is html/text I need to make it application/json. I am using iron-router and then the json is displayed through a template. I just need to change the response header for that page.
How can I do it?
Here's a simple example using a server-side route:
Router.map(function() {
this.route('jsonExample', {
where: 'server',
path: '/json',
action: function() {
var obj = {cat: 'meow', dog: 'woof'};
var headers = {'Content-type': 'application/json'};
this.response.writeHead(200, headers);
this.response.end(JSON.stringify(obj));
}
});
});
If you add that to your app and go to localhost:3000/json you should see the correct result.

Access-Control-Allow-Origin using ShareJS

I have my website on one domain/server: www.mysite.com and I'm running ShareJS on another server: www.my-other-server.com:8000.
www.mysite.com/index.html
<script src="http://www.my-other-server.com:8000/bcsocket.js"></script>
<script src="http://www.my-other-server.com:8000/share.js"></script>
<script src="http://www.my-other-server.com:8000/textarea.js"></script>
...
<textarea id='sharetext' ></textarea>
<script>
// get the textarea element
var elem = document.getElementById("sharetext");
// connect to the server
var options = {
origin: "www.my-other-server.com:8000",
browserChannel:{cors:"*"}
}
var connection = sharejs.open('test', 'text', options, function(error, doc) {
doc.attach_textarea(elem);
});
</script>
I get the following error in the JS console:
XMLHttpRequest cannot load http://www.my-other-server.com:8000/test?VER=8&MODE=init&zx=v44znr3caqea&t=1. Origin http://www.mysite.com is not allowed by Access-Control-Allow-Origin.
This ShareJS GitHub Issue (https://github.com/share/ShareJS/issues/77) suggests adding browserChannel:{cors:"*"} to the share options, as I did above, but it did not seem to have any effect...
What do I do here? It's important that my sharejs traffic is on a separate server than my static/dynamic web server.
On server side in node.js, if you are using express.js you need to add extra headers, that will allow cross-domain traffic from server side:
app.configure(function() {
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
next();
});
app.set('jsonp callback', true);
});
On client side you still might end up with security issues, so it is even better to use JSONP, so from server side response like that:
res.jsonp({ hello: 'world' });
And on client side AJAX like that:
$.ajax({
url: "www.my-other-server.com:8000",
type: 'GET',
dataType: 'jsonp',
success: function(data) {
console.log(data)
},
error: function(xhr, status, error) {
console.log('error[' + status + '] jsonp');
}
});
Try adding browserChannel: { cors:"*" } in bin/options.js. It should work.
The final options.js may look like this
// ShareJS options
module.exports = {
// Port to listen on
port: 8000,
// Database options
db: {
// DB type. Options are 'redis', 'couchdb' or 'none'. 'redis' requires the
// redis npm package.
//
// If you don't want a database, you can also say db: null. With no database,
// all documents are deleted when the server restarts.
// By default, sharejs tries to use the redis DB backend.
type: 'redis',
// The prefix for database entries
prefix: 'ShareJS:',
// The hostname, port and options to pass to redis.
// null lets the database decide - redis by default connects to localhost port 6379.
//hostname: null,
//port: null,
//redisOptions: null
// To use CouchDB uncomment this section then run bin/setup_couch.
// Database URI Defaults to http://localhost:5984/sharejs .
//type: 'couchdb',
//uri: "http://admin:admin#localhost:5984/ot",
// To use postgresql uncomment this section then run bin/setup_pg
//type: 'pg',
//uri: 'tcp://josephg:#localhost/postgres',
// By default, sharejs will create its tables in a schema called 'sharejs'.
//schema: 'sharejs',
//operations_table: 'ops',
//snapshot_table: 'snapshots',
// sharejs will automatically try and create the DB tables if they don't exist. You
// can create the database tables manually using bin/setup_pg.
//create_tables_automatically: true,
},
// The server will statically host webclient/ directory at /share/*.
// (Eg, the web client can be found at /share/share.js).
// Set staticpath: null to disable.
staticpath: '/share',
// REST frontend options. Set rest: null to disable REST frontend.
rest: {
},
// SocketIO frontend options. Set socketio: null to disable socketIO frontend.
socketio: {
// Specify tuples for io.configure:
// 'transports': ['xhr-polling', 'flashsocket']
},
// Browserchannel server options. Set browserChannel:null to disable browserchannel.
browserChannel: {cors:"*"},
// Authentication code to test if clients are allowed to perform different actions.
// See documentation for details.
//auth: function(client, action) {
// action.allow();
//}
}

Categories

Resources