Bodyparser is incorrectly parsing JSON? - javascript

I'm using the body-parser NPM module with Express to parse json on my server, but for some reason, the JSON is showing up incorrectly on the server. Here is my server code:
...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
...
app.route("/schedule")
.get(function(req, res) {
...
})
.post(function(req, res) {
var schedule = req.body.schedule;
console.log(req.body);
if(schedule) {
setSchedule(schedule);
res.status(200).end();
}
});
And my client code:
var schedule = {
entries: entries
};
var str = JSON.stringify(schedule);
console.log("Submitting schedule:",str);
post("/schedule", str)
.then((res) => {
this.completed(res.json);
})
.catch((res) => {
this.failed(res.text);
});
When I POST the data from the client, the client prints this:
Submitting schedule: {"entries":[1430014800000,1430055600000,1430104620000,1430146380000,1430194140000,1430236920000,1430283120000,1430326860000,1430371740000,1430416380000,1430460180000,1430505480000,1430548500000,1430594460000,1430636760000,1430683260000,1430725020000,1430772060000,1430813340000,1430860920000,1430901720000,1430949900000,1430990340000,1431039060000,1431079200000,1431128520000,1431168480000,1431218220000,1431258360000,1431308160000,1431349020000,1431398040000,1431440220000,1431487800000,1431531360000,1431577260000,1431622140000,1431666540000,1431712440000,1431755640000,1431802320000,1431844680000,1431891960000,1431933660000,1431981360000,1432022580000,1432070700000,1432111560000,1432159980000,1432200540000,1432249260000,1432289580000,1432338600000,1432378860000,1432428060000,1432468500000,1432517520000,1432558560000]}
Which appears to be valid JSON, but on the server, req.body is this:
{ '{"entries":': { '1430014800000,1430055600000,1430104620000,1430146380000,1430194140000,1430236920000,1430283120000,1430326860000,1430371740000,1430416380000,1430460180000,1430505480000,1430548500000,1430594460000,1430636760000,1430683260000,1430725020000,1430772060000,1430813340000,1430860920000,1430901720000,1430949900000,1430990340000,1431039060000,1431079200000,1431128520000,1431168480000,1431218220000,1431258360000,1431308160000,1431349020000,1431398040000,1431440220000,1431487800000,1431531360000,1431577260000,1431622140000,1431666540000,1431712440000,1431755640000,1431802320000,1431844680000,1431891960000,1431933660000,1431981360000,1432022580000,1432070700000,1432111560000,1432159980000,1432200540000,1432249260000,1432289580000,1432338600000,1432378860000,1432428060000,1432468500000,1432517520000,1432558560000]': '' } }
which is an object that's only key is {"entries": and the value for that key is an object that's only key is an array of timestamps that should be sent as the value to entries.
Any help would be greatly appreciated.

It appears the module I'm using for making requests (superagent) automatically stringifies data, so the issue went away when I stopped stopped using JSON.stringify.

In my case, the issue was that I was making the request from the front end using AngularJS, and the default POST format was set to url-encoded:
$httpProvider.defaults.headers.post = {'Content-type': 'application/x-www-form-urlencoded'};
By default, it's set to 'application/json', so I just removed this line, after which the JSON then resolved correctly.

Related

Backend post request only reads as "undefined" JS

I am pretty new to JS so please go easy on me.
Im trying to increment a value in a JSON file in my JS backend application. Whenever I increment the associated value by the key, it creates a new section "undefined"
Here is the entire application:
const { response } = require("express");
const express = require("express");
const fs = require("fs").promises;
const path = require("path");
const app = express();
const dataFile = path.join(__dirname, "data.json");
//support POSTing form data wuth url encoded
app.use(express.json());
app.get("/poll", async (req, res) =>{
//data is now the js object of the Json data json
let data = JSON.parse(await fs.readFile(dataFile, "utf-8"));
const totalVotes = Object.values(data).reduce((total, n) => total +=n, 0);
data = Object.entries(data).map(([label, votes]) => {
return{
label,
percentage: (((100 * votes) / totalVotes) || 0).toFixed(0) // or with 0 in the even that you divide by zero
}
});
res.json(data);
});
app.use(express.urlencoded({ extended: true}));
app.post("/poll", async (req, res) => {
const data = JSON.parse(await fs.readFile(dataFile, "utf-8"));
data[req.body.add]++;
await fs.writeFile(dataFile, JSON.stringify(data));
res.json(data);
});
app.listen(3000, () => console.log("Server is running ..."));
Here is my JSON file before I POST
{
"JavaScript":0,
"TypScript":10,
"Both":3}
Here is a picture of my POST request in Insomnia:
Here is my JSON file after a Post Requests
{
"JavaScript": 0,
"TypScript": 10,
"Both": 3,
"undefined": null
}
Here is is after a second POST request:
{
"JavaScript": 0,
"TypScript": 10,
"Both": 3,
"undefined": 1
}
I know it might be something very simple, but I am very inexperienced so any help would be greatly appreciated!
I tried including some additional middleware to enable bodyParsing as I saw in other posts but that did not fix the issue.
I also tried
console.logging(req.body)
,but that only printed "undefined".
EDIT:
I found the problem, I wasn't sending the information in Insomnia correctly, I need to click the form type and either format a JSON input or select the "Form URL Encoded"" Option. Otherwise it doesn't recognize the format.
Thank you for your help!
I tried your code in my device, it works fine on my device which means you are not properly sending post request.
And there is a bug in your logic of adding name , in which you increment the add property of data object but that property is not a number but a null so the number does not increments. Here is a simple fix...
app.post("/poll", async (req, res) => {
const data = JSON.parse(await fs.readFile(dataFile, "utf-8"));
let {add} = req.body;
if(data[add] === 0 || data[add]) data[add]++;
else data[add] = 0;
await fs.writeFile(dataFile, JSON.stringify(data));
res.json(data);
});
If req.body printed "undefined", req.body.add will also be equal to "undefined".
This is causing the error. I haven't used Insomnia but when you send your POST request your "body" needs to be something like this:
{
"add": "variableName"
}

Why downloading a file from node.js server multiple times results in empty files

I am glad to get some help.
Here is my problem:
I have built a web server with node.js that should send a csv file to the client when requested through a certain route. The csv file is created from json using the fast-csv package. The json data comes from a mongoDB and is processed with mongoose.
When I request this route once, it works fine. However, when it is requested a second time, an empty file is sent to the client. By the way, the headers reach the client correctly.
I have to restart the server to download my file again.
What did I try:
Basically, I have now lost track of everything I have tried. This behavior occurs both when using postman and when querying via the browser.
I've tried implementing promises in my handler function.
I've tried to unsubscribe
res somehow (but yes, that was a stupid approach).
I`ve tried to write the file into the fs and to send it on a second request. ...
Maybe one of you can tell what's going wrong here at first glance:
const { format } = require("#fast-csv/format");
const csvStream = format({ delimiter: ";", headers: true });
const router = express.Router();
router.route("/:collection/csv").get(requireModel, createCsv);
const csvFromDatabase = (data, res) => {
csvStream.pipe(res);
const processData = (data) => {
data.forEach((row) => {
const { _id, __v, ...newRow } = row._doc;
csvStream.write({ ...newRow });
});
csvStream.end();
};
processData(data);
};
const createCsv = async (req, res) => {
const { model } = req;
const items = await model.find({});
res.setHeader("Content-disposition", "attachment; filename=file.csv");
res.setHeader("Content-type", "text/html; charset=UTF-8");
csvFromDatabase(items, res);
};
Thank you very much for your help. I hope I didn't bore you with too stupid questions.
You need to recreate csvStream for each new request:
const csvFromDatabase = (data, res) => {
const csvStream = format({ delimiter: ";", headers: true });
csvStream.pipe(res);
…
};

How do I make a live search result in node.js and mongoDb

I am trying to implement a feature where I have an input on this route to make a live search of employees in the database
app.get('/delete' , isLoggedIn , (req , res) => {
res.render('pages/delete')
})
This route serves the search input. How do I create a live search based on a keyup event listener that sends the data to mongoDb/mongoose to search and return the results on the page?
I know how to do the event listener to get what is typed like so which is in the delete.js file
const deleteSearchInput = document.querySelector('#search-input');
deleteSearchInput.addEventListener('keyup' , (e) => {
let search = e.target.value.trim()
})
How do I send the value "e" to a post route to do the search and return it to the page
AJAX (using the JavaScript fetch API). AJAX allows JavaScript to send requests to the server without reloading.
const deleteSearchInput = document.querySelector('#search-input');
deleteSearchInput.addEventListener('keyup' , (e) => {
let search = e.target.value.trim();
fetch('/delete', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({search})
}).then(res =>
res.json()
).then(data => {
console.log(data.result); // <-- success!
}).catch(err => {
alert('error!');
console.error(err);
});
});
Then you have changes to make to the server side. Since you're sending a POST request, you need to create a handler to POST:
app.post('/delete', isLoggedIn, (req, res) => {
res.send('success!');
});
This will handle post requests, and only post requests. Now to get the value of whatever you sent to the server, we need to use an npm package called body-parser, which parses the incoming request. Run the following command in shell:
npm i body-parser
Then at the top of your server file before declaring your routes import and use the body-parser library:
const bodyParser = require('body-parser');
app.use(bodyParser.json()); // <-- add the JSON parser
Finally change your handler again:
app.post('/delete', isLoggedIn, (req, res) => {
const { search } = req.body;
console.log(search);
// ... do whatever you want and send a response, e.g.:
const result = 'my awesome message';
res.json({ result });
});
And that's how you do it.

Get request doesn't work server side

I am making basic web server using nodejs and express module. It has to be able to respond to POST and GET requests. POST is just working fine, but GETdoesn't return anything. In console there's a textStatus of an error parserror and SyntaxError: Unexpected end of input at Object.parse (native) at jQuery.parseJSON error. I'm new to NodeJS and Express, please tell me where I went wrong.
var express = require('express'),
server = express(),
fs = require('fs');
server.use(express.static('../client'));
server.post('/students.json', function (req, res) {
var bodyStr = '';
req.on('data', function (chunk) {
bodyStr += chunk.toString();
});
req.on('end', function () {
fs.readFile('students.json', function (err, data) {
var encodedObj = data.toString('utf8'), //encoding what's inside of .json into human symbols
parsedObj = JSON.parse(encodedObj);
parsedObj.push(JSON.parse(bodyStr)); //adding newly created parsed obj into array
fs.writeFile('students.json', JSON.stringify(parsedObj), function (err) { //rewriting file with new array
if (err) {
console.log(err);
}
});
});
});
});
server.get('/students.json', function (req, res) {//what's wrong???
res.send();
});
var server = server.listen(8888);
What are you trying to res.send()? It looks empty to me. Try:
res.send('Hello World!'); // A string
...or...
res.send([{'name': 'Joe Student'},{'name': 'Sally Goestoskuhl'}]); // Array
...or...
res.send({}); // Empty json response
...or...
res.send(404); // Any integer is considered an HTTP error code
...or...
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ variable: 'value' }));
...or...
// Assuming your json is in the public folder...
res.sendFile(path.join(__dirname, '../public', 'students.json'));
res.send(); on it's own just sends an empty response.
If you then try to json_decode it, you'll get an error.
If I interpret your question correctly, you want both POST and GET to return the same result?
You could do this pretty simply like this:
function sendJSON(req, res)
{
//JSON code from your existing server.post
}
app.get('/students.json', sendJSON);
app.post('/students.json', sendJSON);

How do I parse my JSON with external middleware now that Express doesn't carry a body parser?

How do I parse my JSON with external middleware now that Express doesn't carry a body parser?
For a while, I was using Express bodyParser to receive and respond to JSON posts to the server. Each time I started up the server, express said something about bodyParser being removed soon, and sure enough, I've updated and now JSON requests seem to be showing null.
So I didn't understand how middleware worked, and had followed an express tutorial to use the body parser. Now, using separate body parser middleware, it seems I'm doing it wrong.
Before, my syntax was:
app.use(express.bodyParser());
Now, with the module body-parser as middleware, it's like this:
app.use(bodyParser.json());
And an example as a whole:
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
function listen() {
app.use(bodyParser.json());
app.post('/login', function (req, res) {
var username = req.body.username;
var password = req.body.password;
console.log('User ' + username + ' is attempting login...');
validate(username, password, function (err, result) {
if (err) loginFail(req, res, err);
else loginSucceed(req, res, result);
});
});
app.listen(3333);
}
listen();
I tried express.json() as the middleware, but that triggers the fatal error:
Error: Most middleware (like json) is no longer bundled with Express
and must be installed separately. Please see
https://github.com/senchalabs/connect#middleware.
That link leads to the body-parser middleware that I'm using via app.use(bodyParser.json()).
Update:
Using bodyParser.json() results in no error, but the data values are null:
User undefined is attempting login...
My client code should be fine, but here it is for completeness:
function sendLogin() {
popLogCreds(creds);
var loginCredentials = {
"username": creds.username,
"password": creds.password
};
console.log("Sending login credentials: " +
JSON.stringify(loginCredentials, null, 4));
request = $.ajax({
url: "http://54.186.131.77:3333/login",
type: "POST",
crossDomain: true,
data: loginCredentials,
dataType: "json",
error: function () {
postError("Uh Oh! The Officeball server is down.");
},
success: function (data) {
var ParsedData = data;
sessionStorage.username = creds.username;
sessionStorage.password = creds.password;
sessionStorage.fname = ParsedData.fname;
sessionStorage.lname = ParsedData.lname;
sessionStorage.rank = ParsedData.rank;
console.log(sessionStorage);
window.location.replace("app.html");
}
});
}
Which results in:
Sending login credentials: {
"username": "jonathan#evisiion.com",
"password": "J******!"
}
And then the result is the POST's error output, which is, as above:
error : function () {
postError("Uh Oh! The Officeball server is down.");
}
Don't take that error message literally. Just means an error happened. The server is, in fact, getting that request, as shown up above.
By default, $.ajax() sends data URL-encoded as mentioned in the description of the processData option:
By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded".
The body-parser that corresponds to that Content-Type and format is urlencoded():
app.use(bodyParser.urlencoded());
If you'd rather use JSON for the request, you'll need to provide the data already formatted as such along with a matching contentType that bodyParser.json() recognizes:
request = $.ajax({
url: "http://54.186.131.77:3333/login",
type: "POST",
crossDomain: true,
data: JSON.stringify(loginCredentials),
contentType: 'application/json',
dataType: 'json'
// ...
});
Note for cross-domain: With these modifications, the server will have to handle preflight OPTIONS requests for the route.
And, note that a bodyParser isn't needed for HEAD or GET requests as the data is included in the URL rather than the body. Express parses that separately into req.query.
In your node code,
make sure you put these two lines of code
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyparser.urlencoded({ extended: false }));
app.use(bodyparser.json());
......then your other code follows..
enjoy...

Categories