Json.parse throws: "SyntaxError: Unexpected end of JSON input" - node.js

Hello fellow stackoverflow members,
The Problem
as described in the titel I have a problem parsing an string to JSON.
The string I want to parse is send by the client to the server with a POST Method. The request is handled by this function on the server:
async function handleReservierung (req: Http.IncomingMessage, res: Http.ServerResponse): Promise<void> {
res.setHeader("Content-Type", "application/json");
res.setHeader("Access-Control-Allow-Headers", "content-type");
res.setHeader("Access-Control-Allow-Origin", "*");
let body: string = "";
req.on("data", chunk => {
body += chunk.toString();
});
req.on("end", () => {
console.log(JSON.parse(body));
res.end(body);
});
}
I've read about incoming POST requests and how to handle them and saw this solution with the incoming chunks added up in the variable body. So far so good.
I thought probably the chunks were not added correctly but as I console.log(body) it would give me a perfectly valid string:
{"ids":["600c4eff6c54c802cc1cac4f","600c4f576c54c802cc1cac51"],"name":"Someons Name"}
like so tested in a JSON validator.
I built an Interface
interface Reservierungen {
ids: number[];
name?: string;
}
which is the one used on the client Side to fill out all the data and then its getting send in the Body of the request with JSON.stringify(body)
When it now ty to JSON.parse(body) on the server it throws this error:
SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at IncomingMessage.<anonymous> (C:\Users\felix\Documents\GIS\GIS-WiSe2020_2021\server\index.js:72:26)
at IncomingMessage.emit (events.js:327:22)
at endReadableNT (_stream_readable.js:1221:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
Why do I need to Parse the JSON?
I want to get the data from the JSON Object to update my MongoDB. I thought I could just parse the JSON string send in the body and use it to update the DB. After being stuck for quite a while I am kindly asking for you advice now :)
Important to know
Due to the limitations of our uni project: not useing any library like express or jquery I am asking for nativ typescript solutions.
I assume something is wrong with the string but I can't figure out what it is. I already tried to trim of the whitespaces which did not help either.
I hope I gave you all the information needed.
Thank you in advance.

I figured it out.
The problem was that I did copy the fetch from MDN and enabled CORS on the fetch() statement .
With CORS enabled there comes a preflight Request (CORS Details) which has an OPTIONS header. My Typescript server wasent configured to accept CORS requests (so no way to handle OPTIONS header) and did try to use the udefined body of the preflight Request.
I disabled the CORS header on the fetch() statement. Which resulted in a working JSON.parse()

Related

How to get response body from Express.js server using Supertest?

I started to write some tests for my application and I have issues to read/get response from the server. I tried many things but nothing really worked, can someone help me please ?
// /api/checkCreds
exports.checkCreds = async function(req, res){
//validation
if(!await User.checkCreds(req.body.username, req.body.password)){
var result = {error: true, data: "Incorrect"}
res.sendStatus = 401;
return res.send(JSON.stringify(result));
}
If credentials sent to the server aren't matching, return a response with "Incorrect" message back to the user.
In the test I'm trying to get data from the server to check if properties are matching the expected output.
//test.js
it("We should fail with HTTP code 401 because incorrect data is passed (username='incorrect' password='incorrect')", function(done){
supertest(app)
.post('/api/checkCreds')
.send({username: 'incorrect', password: 'incorrect'})
.expect({error: true, data: "Incorrect"})
.expect(401, done);
});
When ran, test fails because expected properties are different from the response sent by the server, which is an empty object {}.
Any help is appreciated.
You may try changing your first expect to see if you can coax supertest into showing you the actual body that it's comparing to. For example, expect('')
If that doesn't work, there's a version of expect that accepts a function. In that function, you should be able to print out what you are getting in the response body, ie. console.log(res).
It may be that there's some confusion with the JSON return type-- I haven't used that directly. You could try expecting JSON.
Finally, there's a strange paragraph in the documentation that I don't think applies, but I thought I'd mention:
One thing to note with the above statement is that superagent now sends any HTTP error (anything other than a 2XX response code) to the callback as the first argument if you do not add a status code expect (i.e. .expect(302)).
While trying to fix my issue, I noticed that in the HTTP response, Content-Type header was set to text/plain and my server was returning JSON, so that probably was the thing that confused supertest.
I think that res.send() sets the header to text/plain by default and I had to manually set the header value to application/json by using res.type('json'). At that point I was able to read the response body without an issue.
I also learned that res.json() sets the Content-Type header to application/json by default, so you don't need to do it manually like with res.send().
Working code:
// /api/checkCreds
if(!await User.checkCreds(req.body.username, req.body.password)){
var result = {error: true, data: "Incorrect"}
return res.status(401).json(result);
}
//test.js
it("We should fail with HTTP code 401 because incorrect data is passed (username='incorrect' password='incorrect')", function(done){
supertest(app)
.post('/api/checkCreds')
.set('Content-type', 'application/json')
.send({username: 'incorrect', password: 'incorrect'})
.expect(401)
.expect(function(res){
console.log(res.body);
})
.end(done);
});
Feel free to correct me if I stated something that isn't quite right.

How do I use the the post method with fetch and koa?

This is a function on my front-end that makes the request.
function postNewUser(){
fetch(`http://12.0.0.1:8080/users/test`, {
method: 'POST',
body: {nome: name, email: "test#test.com.br", idade: 20}
})
}
This is my back-end code to receive the request.
router.post('/users/:id', koaBody(), ctx => {
ctx.set('Access-Control-Allow-Origin', '*');
users.push(ctx.request.body)
ctx.status = 201
ctx.body = ctx.params
console.log(users)
})
For some unknown reason I receive nothing. Not even a single error message. The "console.log()" on the back-end is also not triggered, so my theory is that the problem is on the front-end.
Edit
As sugested by gnososphere, I tested with Postman, and it worked. So now i know the problem must be on the fron-end code.
You can try your backend functionality with Postman. It's a great service for testing.
the request would look something like this
If the problem is on the frontend, double check your fetch method by posting to a website that will return data and logging that in your app.

Log request that caused error in node modules

I run a NodeJS server with two new error types in the logs:
[2021-05-21T09:11:33.891Z] SyntaxError: Unexpected token h in JSON at position 0
at JSON.parse (<anonymous>)
at createStrictSyntaxError (~/server/node_modules/body-parser/lib/types/json.js:158:10)
at parse (~/server/node_modules/body-parser/lib/types/json.js:83:15)
at ~/server/node_modules/body-parser/lib/read.js:121:18
at invokeCallback (~/server/node_modules/raw-body/index.js:224:16)
at done (~/server/node_modules/raw-body/index.js:213:7)
at IncomingMessage.onEnd (~/server/node_modules/raw-body/index.js:273:7)
at IncomingMessage.emit (events.js:323:22)
at IncomingMessage.EventEmitter.emit (domain.js:482:12)
at endReadableNT (_stream_readable.js:1204:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
The stacktrace shows only node_modules paths, not where in my code this error may have started. The stdout logs do not show what could have originated this error around that time either.
The server code that handles JSON objects is:
// Use JSON parser (required to parse POST forms)
app.use((req, res, next) => {
bodyParser.json()(req, res, next);
});
app.use(bodyParser.urlencoded({extended: true}));
I added logging inside this function in case I have the same error in the future.
In general, how can I log information about the request that caused an error in the node modules?
update with client-side code
This error originated from a user and I am unable to replicate it. The client-side code sending JSON data is:
// `id` indicates the ID of the video
var body = {
percent: percent,
videoId: id,
eventLabel: eventLabel
}
async function view() {
return await fetch("/viewership", {
method: "POST",
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
};
The Network tab of the Chrome debugger shows this request payload:
{percent: 0, videoId: ..., eventLabel: "play"}
Well, here's what we know.
The code is processing an IncomingMessage (and incoming http request)
The error comes from the body-parser module
The error comes from JSON.parse() on what is apparently supposed to be a JSON body
The error appears to come from this particular section of code in the body-parser module.
That code is this:
if (strict) {
var first = firstchar(body)
if (first !== '{' && first !== '[') {
debug('strict violation')
throw createStrictSyntaxError(body, first)
}
}
So, it is apparently failing to find a leading { or [ on the JSON and is instead finding an h.
We can deduce from that information that an incoming http request (probably a POST) is supposed to have a JSON body, but the data is not legal JSON.
Your first point of debugging is to see exactly the JSON body data is in the request. If this request is coming from a browser, you can look in the Chrome network tab of the debugger and see exactly what the browser is sending your server.
So, this is most likely a client-caused error. Either the content-type is set wrongly to JSON when the data is not JSON or the client is supposed to be sending JSON, but is not sending proper JSON.
If you can show us the client-side code for this, we may be able to spot the error in that code.
Do you know a way to log any request that throws an error, e.g. for other bad requests in the future unrelated to JSON?
When the body-parser gets bad JSON, it calls the Express error handler with the exception. If you go the the "Writing Error Handlers" on this Express doc page, it will show you how to catch these errors and handle them with some error page back to the client and as much logging as you want.

Access individual fields from request body inside cloud function

I'm sending a post request to a cloud function which contains the following body:
{message: "this is the message"}
If I try to print the entire body of the request, it shows it, but if I try to get the message field, I get undefined in the console.
Here's my function:
exports.myCloudFunction = functions.https.onRequest((req: any, res: any) => {
console.log(req.body)\\prints the body of the request fine
console.log(req.body.message)\\prints "undefined"
cors(req, res, () => {
const pl = req.body.message;
console.log(pl);\\prints "undefined"
});
return res.send("all done")
});
You don't need a body parser for Cloud Functions as described in the other answer here. Cloud Functions will automatically parse JSON and put the parsed JSON object in the body attribute. If you want this to happen automatically, you should set the content type of the request to "application/json" as described in the linked documentation. Then you can use req.body as you'd expect.
I personally haven't worked with firebase before, but from your code and the fact that req.body prints the body, it seems that you probably need to parse the request-body as json in order to be able to access the message property:
const body = JSON.parse(req.body);
console.log(body.message);
It could also be the case that you need to setup a bodyparser for json content. In a normal express-app you can do this using (note that you don't need the manual parsing from above anymore using bodyparser):
const bodyParser = require('body-parser');
app.use(bodyParser.json();
EDIT:
see Doug's answer for the correct way to do this, i.e. to fix your request and set the content-type to application/json in order for Cloud Functions to automatically parse and populate req.body with the request body.
You need to just convert the request body into a JSON object in cloud function.
const body = JSON.parse(req.body);
// message
console.log(body.message)

Can't get at Content-Type header using Express 4 Router

I'm trying to return different content, based on the Content-Type header in the request: plain text, or JSON object. In Express 3.x, I used the req.accepted('application/json') to find out if the user asked for JSON. but req.accepted() has been deprecated in 4.x.
I tried req.is() - returned undefined, and req.accepts() - useless. Finally, I resorted to:
var router = require('express').Router();
router.get('/ping', function(req, res) {
var serverTime = (new Date()).toLocaleString();
if(req.get('Content-Type').indexOf('json') !== -1) {
res.set({'Content-Type': 'application/json'});
res.send({serverTime: serverTime});
}
else {
res.send('serverTime: ' + serverTime);
}
});
This works great on localhost (tested with CURL), but once I deployed to Heroku, I'm getting:
TypeError: Cannot call method 'indexOf' of undefined at
Object.router.get.res.set.Content-Type [as handle]
How can I get the header type in Express 4, and handle properly? Is Heroku stripping th header somehow? Or maybe it's those new middlewares?
Update: I just verified that using CURL works every time, using a browser yields undefined for req.get('Content-Type') on both local and Heroku - so this is not a Heroku issue. Still, I need the header.
Checking Content-Type on a GET request does not make sense. The Content-Type header defines the type data in the request body, and a GET request has no body. req.is also checks this, so it is also useless in this case.
You should be setting the Accept header to application/json from the client making the GET and using req.accepts('json') on the server to verify that the client has indicated that it supports JSON.

Resources