I have created a REST endpoint to receive JSON data via an HTTP post, via an XMLHttpRequest as my client. It works fine. However I wanted to test if my client is sending no data at all, i.e. null, so this would be handled gracefully by my server. Except ... it crashes the server with a syntax error when I try to perform a simple test on the request, or even just to echo it out using console.log. I can't seem to work out how to test for the null condition.
The server is Express (v 4.16.4) under node.js (v 11.4.0).
The error I get is:
SyntaxError: Unexpected token n in JSON at position 0
I have done a lot of Googling for this but very little luck with token 'n'.
I have tried: Testing existence of req with
if (req) {
...
or
try {
console.log(req);
}
catch(err) {
console.log("Caught error: "+err);
}
And even a good old:
router.route('/api/endpoint/:filename').post((req, res, err) => {
if (err) {
console.log("Caught error: "+err);
} else {
...
This is a bit worrying, as while I can control what I send to the endpoint from my client, and I know sending a null payload makes no sense and is invalid, what if anyone else (maliciously?) posts null to the API, and trivially breaks the server?
For completeness, here's my client code and server function.
Client:
// Testing sending null data to a REST endpoint
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
var url = "http://localhost:4001/api/endpoint/delme.txt";
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var json = JSON.parse(xhr.responseText);
console.log(json.email + ", " + json.password);
}
};
data = null; // Normally I'd send a JSON object
// e.g. data = {"stack" : "overflow"}
// data = {"stack" : "overflow"};
xhr.send(JSON.stringify(data));
Server function:
router.route('/api/endpoint/:filename').post((req, res) => {
if (req)
console.log("Got req");
else
console.log("nothing...");
console.log("Body....");
console.log(req.body);
....
Working correctly with data = {"stack" : "overflow"}:
Got req
Body....
{ stack: 'overflow' }
Error when I send data = null:
SyntaxError: Unexpected token n in JSON at position 0
at JSON.parse (<anonymous>)
at createStrictSyntaxError (C:\Users\roryc\source\repos\ang09server\node_modules\body-parser\lib\types\json.js:158:10)
....
Thanks for any help
[UPDATE: Providing code samples to help reproduce error]
I wasn't sure if it was relevant, but I am compiling using babel. Here are four code samples:
- package.json
- client.js
- server.js
- .babelrc
I build in the root directory (after npm install) with npm run-script build I execute the server with npm start I execute the client with a simple node dist/client.js
Package.json:
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon --exec babel-node src/server.js",
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "babel-watch src/server.js",
"build": "babel src --out-dir dist",
"serve": "node dist/server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.4",
"xmlhttprequest": "^1.8.0"
},
"devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.4",
"@babel/node": "^7.2.2",
"@babel/preset-env": "^7.4.4",
"nodemon": "^1.19.0"
}
}
Server.js:
import express from 'express';
import bodyParser from 'body-parser';
const app = express();
const router = express.Router();
const http = require('http').Server(app);
app.use(bodyParser.json());
app.use('/', router);
http.listen(4001, function() {
console.log('Express listening (+web sockets) on port 4001');
});
router.route('/api/endpoint/:filename').post((req, res) => {
console.log(req.body); // Error appears here...
// Function logic goes here...
});
Client.js:
// Testing sending null data to a REST endpoint
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
var url = "http://localhost:4001/api/endpoint/delme.txt";
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var json = JSON.parse(xhr.responseText);
console.log(json.email + ", " + json.password);
}
};
var data = null; // Normally I'd send a JSON object, e.g. data = {"stack" : "overflow"}
// var data = [{"stack" : "overflow"}];
xhr.send(JSON.stringify(data));
.babelrc:
// .babelrc
{
"presets": ["@babel/preset-env"]
}
Thanks for the comments and suggestions, very much appreciated.
..................
[SOLVED] [bendataclear]1 provided both the answer and (nearly) working code, which I have made very minor adjustments to and now correctly catches the null data being sent and throws an error without the server failing. Reproducing the solution here in case it helps anyone else. Big thanks to bendataclear.
// Verification function for bodyparser to test for null data being sent
const vfy = (req, res, buf, encoding) => {
console.log("Received buffer: "+buf);
if (buf.equals(Buffer.from([0x6e, 0x75, 0x6c, 0x6c]))){ // Check for characters 'null' in buffer
throw new Error('null body');
}
};
app.use(bodyParser.json({
verify: vfy
}));