Express 4 中间件错误处理程序未被调用

问题描述 投票:0回答:7

对于某些页面,我在应用程序中自定义了 500、404 和 403 错误处理。因此,例如在数据库查询失败之后我会:

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

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

在我的中间件中,我有一个错误处理程序:

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

问题是错误处理程序永远不会被调用,而是错误调用堆栈被打印到浏览器中。我希望处理程序呈现自定义错误页面。

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.

中间件.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
    });
};
node.js express middleware
7个回答
96
投票

问题是,您的错误处理程序应该始终位于应用程序堆栈的末尾。这意味着,您可以将错误处理程序从中间件移动到 app.js 并在 require (app.use()) 之后使用它们,或者在中间件之前包含您的路由。


77
投票

注意:你的错误处理中间件必须有 4 个参数:error、req、res、next。否则你的处理程序不会触发。


19
投票

用于处理 Express 中异步代码执行期间引发的错误(版本 < 5.x), you need to manually catch and invoke the in-built error handler (or your custom one) using the

next()
函数。

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      console.log('Async code execution.')
      throw new Error('Unexpected error on async!')
    } catch (error) {
      // manually catching and propagating to your error handler
      next(error)
    }
  }, 100)
})

// as a last declaration in your entrypoint
app.use((err, req, res, next) => {
  // do some error handling here. if you do not call next, you must return a response from here.
})

18
投票

我遇到了同样的问题,花了一整天的时间来解决问题。最后,找到了一个简单的修复方法。这对我来说非常有效。您需要将客户错误处理程序放置在侦听器处理程序之前,如下所示在服务器实例文件 (App.js / server.js) 上。 祝你好运:)

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;

13
投票

您的错误处理程序应始终位于应用程序堆栈的末尾。 显然,这意味着 不仅是 毕竟

app.use()
而且还 所有
app.get()
app.post()
通话之后。


5
投票

如果您不想为每个异步路由器处理程序编写三个参数以便能够全局捕获错误:

npm install express-async-errors

import 'express-async-errors';

app.get('/api/endpoint', async (req, res) => {
  const user = await User.findByToken(req.get('authorization'));
 
  if (!user) throw Error("access denied"); //will propagate to global error handler
});

0
投票

你必须在堆栈末尾使用错误处理中间件

const userRouter=require('./routers/userRouter');
    app.use('/api/user',userRouter)
    


app.use(notFound);
app.use(errorHandler);



const server = app.listen(
  PORT,
  console.log(`Server running on PORT ${PORT}...`)
);

错误处理中间件

const notFound = (req, res, next) => {
    const error = new Error(`Not Found - ${req.originalUrl}`);
    res.status(404);
    next(error);
  };
  
  const errorHandler = (err, req, res, next) => {
    const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
    res.status(statusCode);
    res.json({
      message: err.message,
      stack: process.env.NODE_ENV === "production" ? null : err.stack,
    });
  };
© www.soinside.com 2019 - 2024. All rights reserved.