web-dev-qa-db-fra.com

Le gestionnaire d'erreurs middleware Express 4 n'est pas appelé

Pour certaines pages, j'ai une gestion des erreurs 500, 404 et 403 personnalisée dans mon application. Ainsi, par exemple, après une requête de base de données infructueuse, j'irais:

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

ou

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

Dans mon middleware, j'ai un gestionnaire d'erreurs:

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

Le problème est que le gestionnaire d'erreurs n'est jamais appelé, à la place la pile d'appels d'erreur est imprimée dans le navigateur. Je souhaite que le gestionnaire affiche une page d'erreur personnalisée.

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
    });
};
26
ChrisRich

Le problème est que vos gestionnaires d'erreurs doivent toujours se trouver à la fin de votre pile d'applications. Cela signifie que vous pouvez soit déplacer le gestionnaire d'erreurs de votre middleware vers votre app.js et les utiliser après vos besoins (app.use ()), soit inclure vos itinéraires avant votre middleware.

53
djKartright

J'ai eu le même problème, passez toute la journée à résoudre le problème. Enfin, trouvé une solution simple. Cela a parfaitement fonctionné pour moi. Vous devez placer le gestionnaire d'erreurs client juste avant le gestionnaire d'écoute comme ci-dessous dans le fichier d'instance de serveur (App.js/server.js). Bonne chance :)

app.use((error, req, res, next) => {

    if (res.headersSent) {
        return next(err)
    }
    res.status(500).send('INTERNAL SERVER ERROR !')
  });

app.listen(3000, function() {
    console.log('Node app is running on port 3000');
});
module.exports = app;
7
Chamath Jeevan