0

Express 4: Middleware для обработки ошибок не вызывается

9

Описание проблемы

У меня есть приложение на Express, в котором реализована кастомная обработка ошибок 500, 404 и 403. Например, после неудачного запроса к базе данных я вызываю:

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

или

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

В моем middleware есть обработчик ошибок:

app.use(function (err, req, res, next) {
    // обработка ошибки
});

Однако возникла проблема: этот обработчик никогда не вызывается. Вместо этого стек ошибок выводится в браузер, и я не могу отобразить собственную страницу с ошибкой.

Структура приложения

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); // сначала загружаю middleware
require('./lib/routes')(app); // затем маршруты

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);
        // Длинный файл с множеством маршрутов. Упрощенная версия.
};

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) {
    // Настройка middleware

    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 часа
        store: new mongoStore({ url: config.getMongoConn() })
    }));

    app.use(logger('dev'));
    app.use(flash());

    // Обработка ошибок
    app.use(function (err, req, res, next) {
        console.error(err.stack);
        // здесь необходимо отрисовать страницу с ошибкой
    });
};

Суть проблемы

Мне нужно, чтобы мой обработчик ошибок вызывался для кастомных ошибок (например, next(new Error('some error')) или next({status: 500, message:'server error'});). Но он никогда не вызывается, и вместо этого ошибка выводится в консоль браузера. Как исправить эту проблему и отобразить свою страницу с ошибкой?

5 ответ(ов)

0

Проблема заключается в том, что ваши обработчики ошибок должны всегда находиться в конце стека вашего приложения. Это означает, что вы можете либо переместить обработчик ошибок из вашего промежуточного ПО (middleware) в файл app.js и использовать его после подключения модулей с помощью app.use(), либо включить ваши маршруты (routes) перед вашим промежуточным ПО.

0

Обратите внимание: ваш обработчик ошибок в промежуточном ПО ДОЛЖЕН иметь 4 параметра: error, req, res, next. В противном случае ваш обработчик не сработает.

0

Для обработки ошибок, которые возникают во время выполнения асинхронного кода в Express (версии < 5.x), вам нужно вручную перехватывать ошибки и вызывать встроенный обработчик ошибок (или ваш собственный) с помощью функции next().

Вот пример кода:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      console.log('Выполнение асинхронного кода.')
      throw new Error('Неожиданная ошибка в асинхронном коде!')
    } catch (error) {
      // вручную перехватываем и передаем в обработчик ошибок
      next(error)
    }
  }, 100)
})

// как последнюю декларацию в вашей точке входа
app.use((err, req, res, next) => {
  // здесь вы можете реализовать обработку ошибок. если не вызовете next, необходимо вернуть ответ отсюда.
})

Данный пример демонстрирует, как правильно обрабатывать ошибки в асинхронном коде с использованием Express. Обратите внимание на необходимость использования try-catch внутри асинхронного вызова для перехвата исключений и передачи их в следующий middleware при помощи next(error).

0

У меня была такая же проблема, я потратил весь день на её решение. В конце концов, я нашёл простое исправление. Это сработало для меня идеально. Вам нужно поместить обработчик ошибок клиента сразу перед обработчиком слушателя, как показано ниже в файле экземпляра сервера (App.js / server.js). Удачи 😃

app.use((error, req, res, next) => {
    if (res.headersSent) {
        return next(err);
    }
    res.status(500).send('ВНУТРЕННЯЯ ОШИБКА СЕРВЕРА!');
});

app.listen(3000, function() {
    console.log('Node-приложение запущено на порту 3000');
});
module.exports = app;
0

Ваш обработчик ошибок должен находиться в конце вашего стека приложений. Это означает, что он должен располагаться не только после всех app.use(), но и после всех ваших вызовов app.get() и app.post(). Таким образом, он будет обрабатывать ошибки, возникшие на всех предыдущих уровнях, и гарантировать, что никакие ошибки не останутся необработанными.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь