I tried to use middleware errorhandler, but doesn't work, even i set process.env.NODE_ENV ='development'
below is the server code:
var express = require('express');
var app = express();
var errorhandler = require('errorhandler');
var notifier = require('node-notifier');
process.env.NODE_ENV = 'development'; //just purposely do this, see if it can work
if (process.env.NODE_ENV == 'developmet') {
app.use(errorhandler({ log: errorNotification }));
}
function errorNotification(err, str, req) {
var title = 'Error in' + req.method + '' + req.url;
notifier.notify({
title: title,
message: str
});
}
app.get('/', function (req, res, next) {
nonexist(); //the error is still captured Native node.js not errorhandler
res.send('this is home page!');
next();
});
app.listen(1338);
no matter what kind of options i tried in errorhandler, it still doesn't work.
can anyone help me to check any setting is wrong?
Error handling is declared after all your other routes. Express moves through routes from top to bottom or left to right (if you imagine all your code on one line).
You can capitalize on this by putting a splat route after all your other routes, and it will be activated if no other exact route matches. This is how you can do an error 404 page.
It's why you build your routes like this (which will prepare you for React Router 'switch component', if you move into React coding):
GET /test/:slug
GET /test
GET /
Here is an example of a splat route and following that, your error handler middleware:
// Try switching the order of these first two
app.get('/', async (req, res, next) => {
return res.status(200).send('test')
})
app.get('*', async (req, res, next) => {
return res.status(404).send('error 404') // res.render('error/404')
})
// ERRORS
app.use(async (err, req, res, next) => {
// if next() is called with a parameter, which can be anything,
// this middleware will fire
res.status(500).send('error 500') // res.render('error/500')
throw err
})
// Try replacing your default route with this now
app.get('/', async (req, res, next) => {
return next('Extreme detonations')
})
You do not need the async functions as I have shown here, but I do it as a matter of convention so I can always slap await in there. I use explicit returns to prevent any issues with sending headers after they are already sent, and because async functions return promises, so explicit return will resolve them.
All my routes generally look like this:
app.get('/admin', async (req, res, next) => {
try {
if (!req.user) throw 'garbageUser'
const poop = await something()
return res.render('template', {
data: obj,
bonusData
})
} catch (e) {
if (e === 'garbageUser') {
log.add(`illegal: ${req.originalUrl} from ${sniffer.getClientIp(req)}`)
return res.render('403')
}
return next(e)
}
})
This should hopefully be informative for you and offer some code to forensically analyze. The Express error handler middleware takes a 4th parameter in the first position called err which contains the value passed into next().
Check out the Express documentation again after studying this, and it will make much more sense :)
To answer your question:
var express = require('express');
var app = express();
// You can add these back now that you understand
// var errorhandler = require('errorhandler');
// var notifier = require('node-notifier');
function handleErrors(error) {
console.log('I'm telling your mom about this: ' + error);
}
app.get('/', function(req, res, next) {
return next('REALLY BAD');
return res.send('this is home page!');
});
// Remember, this must be after all your other routes
app.use(function(err, req, res, next) {
console.log('Problem occurred, we could put logic here...');
console.log('Error was: ' + err);
if (err === 'REALLY BAD') {
handleErrors(err);
}
next();
});
app.listen(1338);
Try commenting this out now return next('REALLY BAD'); and run it again. You should see "this is home page!".
When you call next() with no parameter in it, Express treats it as no error. If you pass any value in, like next(err) or next('Chicken tastes good'), you will see err defined with that value in your error handling middleware.
Related
Environment: node.js, Express
I'm attempting to use an error handling pattern based on work done by Valeri Karpov (creator of Mongoose). He explains the pattern that he uses in this article, The 80/20 Guide to Express Error Handling.
In the simplified server below I can successfully feed errors through to my error handling middleware.
const express = require('express');
const app = express();
app.set('view engine', 'ejs');
app.get('/', async function(req, res, next) {
let catStatus = false;
function answer(X) {
return new Promise( function(resolve, reject) {
if(X) {
resolve('cat does exist');
} else {
reject('whoops, cat does not exist');
}
});
}
answer(catStatus)
.then( function(data) {
res.render('index', { output: data });
})
.catch( function(error) {
next(new Error(error));
});
});
app.use( function(error, req, res, next) {
res.render('error', { error });
});
app.listen(8080, function(){
console.log('listening on port 8080');
});
However I'm stuck on how to implement his wrapper pattern with my basic setup. Does the code inside my '/' endpoint go inside of his '*' endpoint?
If so does anything go inside of his function wrapAsync(fn)? Should I just delete his 2 comment lines and leave it as is?
app.get('*', wrapAsync(async function(req, res) {
await new Promise(resolve => setTimeout(() => resolve(), 50));
// Async error!
throw new Error('woops');
}));
app.use(function(error, req, res, next) {
// Gets called because of `wrapAsync()`
res.json({ message: error.message });
});
app.listen(3000);
function wrapAsync(fn) {
return function(req, res, next) {
// Make sure to `.catch()` any errors and pass them along to the `next()`
// middleware in the chain, in this case the error handler.
fn(req, res, next).catch(next);
};
}
I think the * endpoint in app.use() is just a wildcard saying to route all incoming requests to that code, similar to the way your / is working.
But yes, you are understanding correctly. Basically, he is saying that any middleware which makes asynchronous requests should use this wrapAsync function. You can remove the comment lines in the wrapAsync implementation if you want.
With the wrapAsync function, you can pass your own async middleware functionality into this wrapper, and it will make sure to call your async middleware and .catch() the promise to call next, so that you don't have to worry about that detail when writing code. You can simply throw errors, and then async wrapper will handle the Express requirement of calling next() when async code fails.
app.use("/", (req, res, next) => {
verifyRequestorPermissionsAsync(req.params)
.catch(err => {
return next(err);
});
});
With the wrapAsync middleware, you will automatically call next with the rejected error (if there is one), so you can clean up the code quite a bit (as well as avoid accidentally forgetting to call next on a rejection).
app.use("/", wrapAsync(async(req, res, next) => {
await verifyRequestorPermissionsAsync(req.params);
}));
These two docs / articles will help clear things up a lot, if I am guessing correctly where the confusion is coming from:
Why do we even care about next and passing a value into that function: https://expressjs.com/en/guide/error-handling.html
What is this wrap function doing, which returns a function that calls your function? https://eloquentjavascript.net/03_functions.html#h_hOd+yVxaku
So my routes are ordered like shown bellow
verifyToken middleware is being used in a lot of routes.
Generally if an error occurs i want the global error handler of index.js to handle it.
But if the error occurred while verifyToken middleware is being used by the login.html route with method = get i would like
to handle it inside routers/user.js which i thought i could do by using router.get(/\/login(\.html)?$/, (error, req, res, next) => {} but the error bypasses it and moves to global error handler.
index.js
const userRouter = require('./routers/user')
app.get('', (req, res) => {
res.render('index')
})
app.use(userRouter)
app.use((req, res, next) => {
res.status(404).redirect('/404.html');
})
//Global error handling
app.use( (error, req, res, next) => {
switch(error.name) {
case "UnauthorizedError":
console.log("UnauthorizedError = ", error.message)
res.status(401).redirect('/401.html');
break
case "InternalServerError":
console.log("InternalServerError = ", error.message)
res.status(500).send('whatever')
break
default:
console.log("Another error = ", error)
}
})
/routers/user.js
const verifyToken = require('../middleware/authentication/verifyToken')
router.get(/\/login(\.html)?$/, verifyToken, (req, res) => {
// If he is already logged in redirect him to dashboard
// This route works as expected
res.redirect('/admin/dashboard.html')
});
router.get(/\/login(\.html)?$/, (error, req, res, next) => {
// If error = Unauthorized
// which means that he is not logged in proceed
if(error.name === 'UnauthorizedError') res.render('login')
// else pass error to global error handler (at index.js)
else next(error)
});
module.exports = router
/middleware/authentication/verifyToken.js
const jwt = require('jsonwebtoken')
var createError = require('http-errors')
const verifyToken = async (req, res, next) => {
try {
// Do some stuff
if (token_doesnt_exist) return next(createError(401, 'TOKEN NOT FOUND', {expose: false}))
// Do some stuff
next()
} catch {
next(createError(e.status, e.message, {expose: false}))
}
})
module.exports = verifyToken
UPDATE
I ended up transforming
router.get(/\/login(\.html)?$/, (error, req, res, next) => {}
to
router.use((error, req, res, next) => {}
which i guess works since it only catches errors from the above route.
I'm not sure if this is the best way i'd really like to see an alternative.
const verifyToken = require('../middleware/authentication/verifyToken')
router.get(/\/login(\.html)?$/, verifyToken, (req, res) => {
// If he is already logged in redirect him to dashboard
// This route works as expected
res.redirect('/admin/dashboard.html')
});
router.use((error, req, res, next) => {
// If error = Unauthorized
// which means that he is not logged in proceed
if(error.name === 'UnauthorizedError') res.render('login')
// else pass error to global error handler (at index.js)
else next(error)
});
module.exports = router
Since you want to catch errors ONLY in that route you can catch the error in the middleware itself:
const verifyToken = async (req, res, next) => {
try {
//middleware logic
} catch(err) {
//error handling logic
}
Maybe not SUPER elegant but it works..
I am new to node and express. I have seen app.get and app.post examples using both "res.send" and "return res.send". Are these the same?
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.type('text/plain');
res.send('i am a beautiful butterfly');
});
or
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.type('text/plain');
return res.send('i am a beautiful butterfly');
});
The return keyword returns from your function, thus ending its execution. This means that any lines of code after it will not be executed.
In some circumstances, you may want to use res.send and then do other stuff.
app.get('/', function(req, res) {
res.send('i am a beautiful butterfly');
console.log("this gets executed");
});
app.get('/', function(req, res) {
return res.send('i am a beautiful butterfly');
console.log("this does NOT get executed");
});
I would like to point out where it exactly made a difference in my code.
I have a middleware which authenticates a token. The code is as follows:
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1] || null;
if(token === null) return res.sendStatus(401); // MARKED 1
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if(err) return res.sendStatus(403); // MARKED 2
req.user = user;
next();
});
}
On the // MARKED 1 line, if I did not write return, the middleware would proceed and call next() and send out a response with status of 200 instead which was not the intended behaviour.
The same goes for like // MARKED 2
If you do not use return inside those if blocks, make sure you are using the else block where next() gets called.
Hope this helps in understanding the concept and avoiding bugs right from the beginning.
app.get('/', function(req, res) {
res.type('text/plain');
if (someTruthyConditinal) {
return res.send(':)');
}
// The execution will never get here
console.log('Some error might be happening :(');
});
app.get('/', function(req, res) {
res.type('text/plain');
if (someTruthyConditinal) {
res.send(':)');
}
// The execution will get here
console.log('Some error might be happening :(');
});
To Add a little bit more context to the examples above. Express has layers. So if you return in your function you end the execution. If you not end that you can go further in your layered logic.
So the next function passed to each layer can be called to execute the next layer. If you don't call next the execution stops after your method excuted is executed. (Return just exits the function)
The response object is still available after sending. It is just not possible to write to it again because it has already completed after you did res.end() or res.send().
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
// a controller handles a http request and terminat it
const controller = (req, res, next) => {
// return http response to client
res.send('hello world');
// do something after you sended request
console.log('do something else');
// if you call next the request will go to the next layer -> afterSend,
// if you do not call next the execution will end
next();
};
// this middleware/layer is executed after response is send to client
const afterSend = (req, res, next) => {
// do something after you sended request, but not send again -> readonly
console.log(res);
// this would throw an error
// res.send()
// res.end()
// etc...
};
// we skip routers here
app.get('/hello', controller, afterSend);
app.listen(port, () => {
console.log(`Running on ports ${port}`);
});
Here's my code
mysql = require('mysql');
con = mysql.createConnection(connectionSetttings);
app.get('/users/list', ensureLogin, function (req, res, next) {
con.query(query.getUserList, [req.user.innId], function (err, rows) {
if (err) {
next(err); // also tried: return next(err)
} else {
displayUsers();
}
});
}, function (err) {
console.error(err);
displayErrorPage(500);
});
I run it with the MySQL server not running to see if Express would render the error page or not. The problem is, Express simply barfs up the stack into the browser. Is there anything wrong with my code? How do I properly chain middlewares?
Define error-handling middleware functions in the same way as other middleware functions, except error-handling functions have four arguments instead of three: (err, req, res, next).
Make sure to add the error handling to the app afterthe routes.
var app = require('express')();
app.get('/users/list', ensureLogin, function (req, res, next) {
con.query(query.getUserList, [req.user.innId], function (err, rows) {
if (err) {
next(err);
} else {
displayUsers();
}
});
});
// Error handling middleware
app.use(function (err, req, res, next) {
displayErrorPage(500);
});
When using MySQL as a session store, session-related middlewares attached using app.use() will intercept requests for all routes and malfunctions. This will make seemingly unrelated routes throw HTTP500 even if it doesn't look like they invoke return next(err).
The solution to this is to only attach any session-related middleware to necessary routes. Alternatively, you can wrap them in a filter function like this one:
var middlewareFilter = function(middleware) {
// Return whatever function that is returned by this function
// to be used in app.use()
return function (req, res, next) {
// Set the routes to be ignored.
var exceptionList = [
'images',
'stylesheets',
'scripts'
];
// Cast req.url to string so it can use JS's string methods
var path = String(req.url);
// If path matches any of the exceptionList...
if (array.contains(path.split('/')[1], exceptionList)) {
// ...just do nothing
return next();
} else {
// Otherwise, return the middleware
return middleware(req, res, next);
}
}
}
And use them like app.use(session(sessionConfig));
I'm trying to get error handling running with express but instead of seeing a response of "error!!!" like I expect I see "some exception" on the console and then the process is killed. Is this how error handing is supposed to be setup and if so is there another way to catch errors?
var express = require('express');
var app = express();
app.use(function(err, req, res, next) {
console.log("error!!!");
res.send("error!!!");
});
app.get('/', function(request, response) {
throw "some exception";
response.send('Hello World!');
});
app.listen(5000, function() {
console.log("Listening on 5000");
});
An example app/guide on error handling is available at
https://expressjs.com/en/guide/error-handling.html
However should fix your code:
// Require Dependencies
var express = require('express');
var app = express();
// Middleware
app.use(app.router); // you need this line so the .get etc. routes are run and if an error within, then the error is parsed to the next middleware (your error reporter)
app.use(function(err, req, res, next) {
if(!err) return next(); // you also need this line
console.log("error!!!");
res.send("error!!!");
});
// Routes
app.get('/', function(request, response) {
throw "some exception";
response.send('Hello World!');
});
// Listen
app.listen(5000, function() {
console.log("Listening on 5000");
});
A few tips:
1) Your code wasn't working because your error handler middleware was run before your route was reached, so the error handler never had a chance to have the error passed to it. This style is known as continuation passing. Put your error handler last in the middleware stack.
2) You should shut down the server when you have an unhandled error. The best way to do that is to call server.close(), where server is the result of doing var server = http.createServer(app);
Which means, you should do something like this:
var server = http.createServer(app);
app.use(function(err, req, res, next) {
console.log("error!!!");
res.send("error!!!");
server.close();
});
You should probably also time out the server.close(), in case it can't complete (your app is in an undefined state, after all):
var server = http.createServer(app);
app.use(function(err, req, res, next) {
console.log("error!!!");
res.send("error!!!");
server.close();
setTimeout(function () {
process.exit(1);
}, 3*1000);
});
I made a library that does all this for you, and lets you define custom responses, including specialized error views, static files to serve, etc...:
https://github.com/ericelliott/express-error-handler
I had the same problem and couldn't figure out what was wrong.
The thing is if you have the express errorHandler defined then your custom error handler is never being called.
If you have the next code, simply remove it:
if ('development' === app.get('env')) {
app.use(express.errorHandler());
}
Worked for me:)
Installing express install connect-domain, then something like this:
var express = require("express"),
connectDomain = require("connect-domain"),
app = express(),
errorHandler;
// Our error handler
app.use(connectDomain());
errorHandler = function (err, req, res, next) {
res.send(500, {
"status": "error",
"message": err.message
});
console.log(err);
};
Then when setting up your endpoints, tack errorHandler on the end in a use():
app.get("/some/data", function (req, res) {
// ... do some stuff ...
res.send(200, "Yay! Happy Success!");
}).use(errorHandler);
Create an error function:
function throwError(status, code, message) {
const error = new Error(message);
error.name = '';
error.status = status;
error.code = code;
throw error;
}
e.g.
throwError(422, 'InvalidEmail', '`email` should be a valid email address')
We assign name to '' so that when we toString the error it doesn't prepend it with "Error: "
As mentioned if you're using express you can create a special error handling middleware by specifying 4 arguments:
app.use((err, req, res, next) => {
if (err) {
res.status(err.status || 500).json({ code: err.code || 'Error', message: err.toString() });
}
});
If you're not using express or otherwise prefer, add that code to your catch handler instead.
Why?
Modern apps handle JSON, throwing errors in JSON makes more sense and results in cleaner UI code.
You should not only throw error messages because they are imprecise to parse. What if the UI is a multilingual app? In that case they can use the code to show a localized message.
Found that recipes from here and even in official documentation brake logging for advanced loggers like pino-http (at least for latest express4). Problem appears when you write like this:
app.use((err, req, res, next) => {
if (!err) {
return next();
}
res.status(err.status || 500).json({ error });
});
Express "thinks" that it's normal result and logger does not log error.
{
...
"res":{"status":400},
"msg":"request completed"
}
Fix here (the first line):
res.err = err;
res.status(err.status || 500).json({ error });
Log output after the fix:
{
...
"res":{"status":400},
"msg":"request errored",
"err":{"type":"Error","message":"Some error","stack":"..skip.."}
}