I'm trying to work up a proof of concept wherein I receive a POST request that then triggers a different POST request. I thought I would setup a nodejs/express server to test this, but the POST request I'm receiving has Content-Type application/json and a body with data that is (I believe) incorrectly formatted JSON.
e.g. { something: "data" }
I can't change this request, and I just need to use the data to send a different request, but Node throws an error: "Unexpected token in json at position" everytime it receives the request. I'm sure this is because the content is marked as JSON, but the body isn't formatted correctly; I just don't know enough about node/express to know how to deal with it. I looked at bodyParser and writing my own middleware function, but I could really use some direction.
Following DamiToma's advice I added error handling, but for some reason the middleware isn't being called:
const express = require('express');
const router = express.Router();
module.exports = router;
router.use((error, req, res, next) => {
console.log("Error handling middleware called...");
if(error instanceof SyntaxError) {
switch(error.type) {
case 'entity.parse.failed':
// JSON is incorrect
console.log('JSON is incorrect');
break;
default:
// other error
break;
}
res.status(400).end();
} else {
next();
}
});
// POST
router.post('/post', (req, res) => {
res.send('POST');
console.log(req.body);
});
If I put the error handling on the app rather than router, it works, I'm just not sure why it doesn't apply when I put in on the router.
Using express, you can setup a middleware to catch this kind of errors
router.use((error, req, res, next) => {
if(error instanceof SyntaxError) {
switch(error.type) {
case 'entity.parse.failed':
// JSON is incorrect
break;
default:
// Some other kind of error happened
break;
}
res.status(400).end();
} else {
next();
}
})
I am using app.all(*) in my express route to send pagenotfound.html page. So at the end of all of my routes, I have:
app.all('*',security.isLoggedIn, (req, res, next) => {
res.render("pageNotFound.ejs")
})
I was sending a post request using jquery ajax and found out that if the route to the post request does not exist it will send the entire html page to my success callback function.
$.ajax({
type:"POST",
url:"/urlThatDoesNotExist",
success:function(data){
//data = ENTIRE HTML FILE
}
})
I know I can change my catch all in express to send something like:
res.status(404).json({
status: 'fail',
message: `Can't find ${req.originalUrl} on this server!`
})
But that would mean that I cannot show themthe pageNotFound.html page.
Is there a way to catch all post route and catch all get route separately? I am not sure how you guys handle it in your application.
You can use app.use().
All you need to do is to put this at the end of all your routes and handle it there.
You can check which method is it with the req.method and then handle it.
app.use((req, res, next) => {
if (req.method === 'GET')
handleGet()
else if (req.method === 'POST')
handlePost()
else
handleRequests()
})
I am getting this error when making a get request to a route that uses middleware. The route verifies the user has a valid token and displays a message and some basic user information.
The information is sent to the end-user just fine, however, I keep seeing these "can't set header errors" in the node console. I believe this has to do with a misusage of the next() function.
Here is my code:
user.js (router)
router.get('/me', VerifyToken, userController.me_get);
VerifyToken.js (middleware)
module.exports = (req, res, next) => {
var token = req.headers['authorization'];
//Check if token used is undefined
if(typeof token !== 'undefined') {
jwt.verify(token, 'secretkey', (err, authData) => {
if(err) {
return next(res.json({ message : 'You are not authorized to be here'}));
} else {
var usertoken = {
id : authData.user._id,
username : authData.user.username
}
res.locals.usertoken = usertoken;
return next();
}
});
return next();
} else {
//Forbidden
return next(res.sendStatus(403));
}
}
users.js (controller)
module.exports.me_get = (req, res, next) => {
return res.json({ message : 'You have arrived!', usertoken : res.locals.usertoken });
}
I just want to get rid of that "Error: Can't set headers after they are sent." in the console.
Any help is appreciated!
You shouldn't call .next if you're ending the request. Otherwise it will go to the next middleware, which will end the request again, and you will get Error: Can't set headers after they are sent error, because you can't end a request twice.
Replace:
return next(res.sendStatus(403));
With
return res.sendStatus(403);
And
return next(res.json({ message : 'You are not authorized to be here'}));
With
return res.json({ message : 'You are not authorized to be here'});
If you send a response with res.json() (or any other method that sends the response), don't call next(). next() will continue processing to other request handlers, some of which may try to send another response. Since you can only send one response per request, you get the error you see.
Once you've sent the response, just return. All processing is done at that point.
For example, replace this:
return next(res.json({ message : 'You are not authorized to be here'}));
with this:
res.json({ message : 'You are not authorized to be here'});
return;
Turns out I had an extra return next() at the bottom of my if block in the middleware.
I would like to know how I can move the following code into a separate function (in the same file) and call upon it when either I call the POST or PUT routes to add or update documents.
I'm using https://www.npmjs.org/package/express-validator
The following is currently in my POST route but when I'm updating a record the title will still need to be validated.
app.post('/docs', auth, function (req, res) {
req.checkBody('title', 'Title is required').notEmpty();
var errors = req.validationErrors();
if(errors){
res.json(400, { errors: errors });
return;
}
//go ahead and save the document
});
I've tried making my own function but I'm not sure where to put the var errors = req.validationErrors(); or whether it's bad practice to return 400 errors from a separate function.
Any help/code much appreciated.
The body of the middleware function is almost identical to the code you are using right now, only with two notable differences:
The function ensures that the req.method is either POST or PUT.
The next() function is called when validation passes. This will trigger the next middleware function in the chain, or the route handler.
app.use('/docs', function(req, res, next) {
if (req.method == 'POST' || req.method == 'PUT') {
req.checkBody('title', 'Title is required').notEmpty();
var errors = req.validationErrors();
if (errors) {
res.json(400, { errors: errors });
return;
}
}
next();
});
app.post('/docs', auth, function (req, res) {
// go ahead and save the document
});
My node.js app is modeled like the express/examples/mvc app.
In a controller action I want to spit out a HTTP 400 status with a custom http message.
By default the http status message is "Bad Request":
HTTP/1.1 400 Bad Request
But I want to send
HTTP/1.1 400 Current password does not match
I tried various ways but none of them set the http status message to my custom message.
My current solution controller function looks like that:
exports.check = function( req, res) {
if( req.param( 'val')!=='testme') {
res.writeHead( 400, 'Current password does not match', {'content-type' : 'text/plain'});
res.end( 'Current value does not match');
return;
}
// ...
}
Everything works fine but ... it seems not the the right way to do it.
Is there any better way to set the http status message using express ?
None of the existing answers accomplish what the OP originally asked for, which is to override the default Reason-Phrase (the text appearing immediately after the status code) sent by Express.
What you want is res.statusMessage. This is not part of Express, it's a property of the underlying http.Response object in Node.js 0.11+.
You can use it like this (tested in Express 4.x):
function(req, res) {
res.statusMessage = "Current password does not match";
res.status(400).end();
}
Then use curl to verify that it works:
$ curl -i -s http://localhost:3100/
HTTP/1.1 400 Current password does not match
X-Powered-By: Express
Date: Fri, 08 Apr 2016 19:04:35 GMT
Connection: keep-alive
Content-Length: 0
You can check this res.send(400, 'Current password does not match')
Look express 3.x docs for details
UPDATE for Expressjs 4.x
Use this way (look express 4.x docs):
res.status(400).send('Current password does not match');
// or
res.status(400);
res.send('Current password does not match');
You can use it like this
return res.status(400).json({'error':'User already exists.'});
One elegant way to handle custom errors like this in express is:
function errorHandler(err, req, res, next) {
var code = err.code;
var message = err.message;
res.writeHead(code, message, {'content-type' : 'text/plain'});
res.end(message);
}
(you can also use express' built-in express.errorHandler for this)
Then in your middleware, before your routes:
app.use(errorHandler);
Then where you want to create the error 'Current password does not match':
function checkPassword(req, res, next) {
// check password, fails:
var err = new Error('Current password does not match');
err.code = 400;
// forward control on to the next registered error handler:
return next(err);
}
At server side(Express middleware):
if(err) return res.status(500).end('User already exists.');
Handle at Client side
Angular:-
$http().....
.error(function(data, status) {
console.error('Repos error', status, data);//"Repos error" 500 "User already exists."
});
jQuery:-
$.ajax({
type: "post",
url: url,
success: function (data, text) {
},
error: function (request, status, error) {
alert(request.responseText);
}
});
When using Axios you can retrieve the custom response message with:
Axios.get(“your_url”)
.then(data => {
... do something
}.catch( err => {
console.log(err.response.data) // you want this
})
...after setting it in Express as:
res.status(400).send(“your custom message”)
My use-case is sending a custom JSON error message, since I'm using express to power my REST API. I think this is a fairly common scenario, so will focus on that in my answer.
Short Version:
Express Error Handling
Define error-handling middleware like other middleware, except with
four arguments instead of three, specifically with the signature (err,
req, res, next). ... You define error-handling middleware last, after
other app.use() and routes calls
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
});
Raise errors from any point in the code by doing:
var JSONError = require('./JSONError');
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Long Version
The canonical way of throwing errors is:
var err = new Error("Uh oh! Can't find something");
err.status = 404;
next(err)
By default, Express handles this by neatly packaging it as a HTTP Response with code 404, and body consisting of the message string appended with a stack trace.
This doesn't work for me when I'm using Express as a REST server, for example. I'll want the error to be sent back as JSON, not as HTML. I'll also definitely not want my stack trace moving out to my client.
I can send JSON as a response using req.json(), eg. something like req.json({ status: 404, message: 'Uh oh! Can't find something'}). Optionally, I can set the status code using req.status(). Combining the two:
req.status(404).json({ status: 404, message: 'Uh oh! Can't find something'});
This works like a charm. That said, I find it quite unwieldy to type every time I have an error, and the code is no longer self-documenting like our next(err) was. It looks far too similar to how a normal (i.e, valid) response JSON is sent. Further, any errors thrown by the canonical approach still result in HTML output.
This is where Express' error handling middleware comes in. As part of my routes, I define:
app.use(function(err, req, res, next) {
console.log('Someone tried to throw an error response');
});
I also subclass Error into a custom JSONError class:
JSONError = function (status, message) {
Error.prototype.constructor.call(this, status + ': ' + message);
this.status = status;
this.message = message;
};
JSONError.prototype = Object.create(Error);
JSONError.prototype.constructor = JSONError;
Now, when I want to throw an Error in the code, I do:
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Going back to the custom error handling middleware, I modify it to:
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
}
Subclassing Error into JSONError is important, as I suspect Express does an instanceof Error check on the first parameter passed to a next() to determine if a normal handler or an error handler must be invoked. I can remove the instanceof JSONError check and make minor modifications to ensure unexpected errors (such as a crash) also return a JSON response.
If your goal is just to reduce it to a single/simple line, you could rely on defaults a bit...
return res.end(res.writeHead(400, 'Current password does not match'));
Well in the case of Restify we should use sendRaw() method
Syntax is:
res.sendRaw(200, 'Operation was Successful', <some Header Data> or null)