Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Express 4 middleware error handler not being called

For certain pages I have custom 500, 404 and 403 error handling in my app. So for instance after an unsuccessful database query I'd go:

return next({status: 404, message: 'Record not found'}); 

or

return next(new Error('Bad things have happened')}); 

In my middleware I have an error handler:

app.use(function (err, req, res, next) {     // handle error }); 

Problem is that the error handler is never called, instead the error callstack is being printed into the browser. I want the handler to render a custom error page.

app.js

var express = require('express')     , app = express()     , swig = require('swig')     , config = require('./lib/config')     , env = process.env.NODE_ENV || 'development'     , path = require('path');  config.configure(env);  app.engine('html', swig.renderFile); app.set('view cache', false);  swig.setDefaults({     cache: config.get('swigCache') });  app.set('view engine', 'html'); app.set('views', __dirname + '/lib/views');  require('./lib/util/swig'); require('./lib/initialisers/mongodb')(); require('./lib/initialisers/aws')(); require('./lib/middleware')(app); // first load middleware require('./lib/routes')(app); // then routes  var server = app.listen(config.get('port'), function() {     console.info('config: ' + JSON.stringify(config.getCurrent()));     console.info('NODE_ENV: ' + env);     console.info('server running: ' + JSON.stringify(server.address())); }); 

routes.js

module.exports = function(app){      app.get('/', require('./views/').index);     app.get('/blog', require('./views/blog').index);     app.get('/blog/:slug', require('./views/blog').getBySlug);      app.route('/report/:slug')         .get(require('./views/report/').index)         .post(require('./views/report/').doReport);          // Very long file with tons of routes. Simplified version. 

middleware.js

var express = require('express')     , app = express()     , path = require('path')     , logger = require('morgan')     , cookieParser = require('cookie-parser')     , bodyParser = require('body-parser')     , passport = require('passport')     , session = require('express-session')     , mongoStore = require('connect-mongo')(session)     , compression = require('compression')     , favicon = require('serve-favicon')     , config = require('./config')     , flash = require('connect-flash')     , multer = require('multer')     , csrf = require('csurf');  module.exports = function(app) {      app.use(bodyParser.urlencoded({ extended: false }))     app.use(bodyParser.json());     app.use(cookieParser());     app.use(csrf({ cookie: true }));     app.use(express.static(path.join(__dirname, config.get('staticContentPath')), {         maxAge: (60 * 60 * 24) * 1000     }));      app.use(session({         resave: true,         saveUninitialized: true,         secret: 'da755fc0-6882-11e4-9803-0800200c9a66',          cookie: {             maxAge: 24 * 60 * 60 * 1000 // 24 hrs         },          store: new mongoStore({             url: config.getMongoConn()         })     }));      app.use(logger('dev'));     app.use(flash());      /**      * 301 redirects      */     app.use(function(req, res, next) {          var host = req.get('host');          // AWS IP --> http         if (host == 'xx.xxx.xxx.xxx') {             return res.redirect(301, config.get('url') + req.originalUrl);         }          // AWS origin --> http         if(host == 'xxx-xxx-xxx-xxx-xxx.ap-southeast-2.compute.amazonaws.com'){             return res.redirect(301, config.get('url') + req.originalUrl);         }          // www --> http         if (/^www\./.test(host)) {             host = host.substring(4, host.length);             return res.redirect(301, req.protocol + '://' + host + req.originalUrl);         }          // Trailing slash --> http         if (req.path.substr(-1) == '/' && req.path.length > 1) {             var query = req.url.slice(req.path.length);             return res.redirect(301, req.path.slice(0, -1) + query);         }          next();     });      // Delete expired Mongo sessions from DB     app.use(function (req, res, next) {         req.session._garbage = new Date();         req.session.touch();         next();     });      /**      * Setting Cache control header for Ajax requests to 30 minutes      */     app.use(function (req, res, next) {          if(req.xhr){             res.header('Cache-Control', 'max-age=' + 1800 + ', public');         }          next();     });      app.use(compression());      app.use(         multer({             dest: config.get('uploads').folders.temp         })     );      app.use(passport.initialize());     app.use(passport.session());     var initPassport = require('./passport/init');     initPassport(passport);      app.use(function (req, res, next) {          res.locals = {             root : 'http://' + req.headers.host,             sitename : require('./config').get('sitename'),             config: config.get('env'),             url : config.get('url'),             user : req.user,             flash : req.flash()         };          next();     });      app.use(function (err, req, res, next) {          if (err.code !== 'EBADCSRFTOKEN'){             return next(err);         }          if(req.xhr){             return res.ok({payload: null}, '403 invalid csrf token');         }          // TODO handle CSRF token errors here         res.status(403);         res.send('form tampered with')     });      // This is never called when throwing errors like     // next(new Error('some error') or     // next({status: 500, message:'server error'});     app.use(function (err, req, res, next) {         console.error(err.stack);         // render an error page     }); }; 
like image 771
ChrisRich Avatar asked Apr 17 '15 13:04

ChrisRich


1 Answers

The problem is, that your error handlers should always be at the end of your application stack. This means, that you can either move the error handler from your middleware to your app.js and use them after your requires (app.use()) or include your routes before your middleware.

like image 180
djKartright Avatar answered Oct 28 '22 23:10

djKartright