Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js - Handle body-parser invalid JSON error

I'm using the body-parser package like this:

// For parsing application/json:
app.use(require('body-parser').json());

// For parsing application/x-www-form-urlencoded
app.use(require('body-parser').urlencoded({ extended: true })); 

When a valid input like { "foo": "bar" } is received everything works fine and I can access the parsed object with req.body.

However, when invalid (non-JSON) data is sent:

data: JSON.stringify("just something inappropriate"),

I get this error:

{ SyntaxError: Unexpected token " in JSON at position 0
    at JSON.parse (<anonymous>)
    at createStrictSyntaxError
    at ...
expose: true,
statusCode: 400,
status: 400,
body: '"Something"',
type: 'entity.parse.failed' }

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ...

How can I handle this properly to prevent the server from shutting down?

like image 744
Raz Buchnik Avatar asked Oct 29 '18 15:10

Raz Buchnik


3 Answers

One option is to add a custom error handler middleware and add a check to catch JSON parsing errors like that one:

app.use(require('body-parser').json()); 
app.use(require('body-parser').urlencoded({ extended: true }));

...

app.use((err, req, res, next) => {
    // This check makes sure this is a JSON parsing issue, but it might be
    // coming from any middleware, not just body-parser:

    if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
        console.error(err);
        return res.sendStatus(400); // Bad request
    }

    next();
});

Another option is to wrap body-parser's middleware to catch errors coming only from there:

const bodyParser = require('body-parser');

app.use((req, res, next) => {
    bodyParser.json()(req, res, err => {
        if (err) {
            console.error(err);
            return res.sendStatus(400); // Bad request
        }

        next();
    });
});

Or if you want to reuse this functionality to catch different errors from different middlewares, you can do:

function handleError(middleware, errorHandler) {
    middleware(req, res, err => err ? errorHandler(err, req, res, next) : next());
}

const bodyParser = require('body-parser');

app.use(handleError(bodyParser.json(), (err, req, res, next) => {
    if (err) {
        console.error(err);
        return res.sendStatus(400); // Bad request
    }

    next();
}));
like image 142
Danziger Avatar answered Oct 16 '22 20:10

Danziger


As of Express 4.16.0 - Release date: 2017-09-28 you can catch different errors from different middlewares splitting the error handler into a different function without using Bodyparser as it is deprecated.

const app = express();

function handleError(middleware, req, res, next) {
  middleware(req, res, (err) => {
    if (err) {
      console.error(err);
      return res.sendStatus(400); // Bad request
    }

    next();
  });
}

app.use((req, res, next) => {
  handleError(express.json(), req, res, next);
});

Notice in the code as of Express 4.16.0 and higher that express:

 app.use(express.json()); // Without middleware error handling.

replaces bodyParser:

 app.use(bodyParser.json()); // Without middleware error handling.
like image 30
Kelvin praises Avatar answered Oct 16 '22 20:10

Kelvin praises


Add an error handler and then customize the behavior of the default way to handle that erorr, the default will be to crash as you describe.

app.use((err, req, res, callback) => {
  // todo: implement error handling logic and return an appropriate response
  console.error(err)
  res.send(500)
  callback()
})
like image 1
justin.m.chase Avatar answered Oct 16 '22 20:10

justin.m.chase