Express 4: Middleware для обработки ошибок не вызывается
Описание проблемы
У меня есть приложение на 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 ответ(ов)
Проблема заключается в том, что ваши обработчики ошибок должны всегда находиться в конце стека вашего приложения. Это означает, что вы можете либо переместить обработчик ошибок из вашего промежуточного ПО (middleware
) в файл app.js
и использовать его после подключения модулей с помощью app.use()
, либо включить ваши маршруты (routes) перед вашим промежуточным ПО.
Обратите внимание: ваш обработчик ошибок в промежуточном ПО ДОЛЖЕН иметь 4 параметра: error, req, res, next. В противном случае ваш обработчик не сработает.
Для обработки ошибок, которые возникают во время выполнения асинхронного кода в 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)
.
У меня была такая же проблема, я потратил весь день на её решение. В конце концов, я нашёл простое исправление. Это сработало для меня идеально. Вам нужно поместить обработчик ошибок клиента сразу перед обработчиком слушателя, как показано ниже в файле экземпляра сервера (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;
Ваш обработчик ошибок должен находиться в конце вашего стека приложений. Это означает, что он должен располагаться не только после всех app.use()
, но и после всех ваших вызовов app.get()
и app.post()
. Таким образом, он будет обрабатывать ошибки, возникшие на всех предыдущих уровнях, и гарантировать, что никакие ошибки не останутся необработанными.
bodyParser устарел в Express 4
Ошибка "npm WARN package.json: Нет поля repository"
Как получить параметры GET после "?" в Express?
Включение HTTPS в express.js
Как вернуть результаты Mongoose из метода find?