Мой вариант использования - отправка настраиваемого сообщения об ошибке JSON, поскольку я использую экспресс для работы моего REST API. Я думаю, что это довольно распространенный сценарий, поэтому остановлюсь на нем в своем ответе.
Укороченная версия:
Экспресс-обработка ошибок
Определите промежуточное ПО для обработки ошибок, как и другое промежуточное ПО, за исключением четырех аргументов вместо трех, в частности, с помощью подписи (err, req, res, next). ... Промежуточное ПО для обработки ошибок определяется последним, после других вызовов app.use () и маршрутизации
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
});
Вызвать ошибки в любом месте кода, выполнив:
var JSONError = require('./JSONError');
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Длинная версия
Канонический способ выдачи ошибок:
var err = new Error("Uh oh! Can't find something");
err.status = 404;
next(err)
По умолчанию Express обрабатывает это, аккуратно упаковывая его как HTTP-ответ с кодом 404 и телом, состоящим из строки сообщения, к которой добавлена трассировка стека.
Это не работает для меня, например, когда я использую Express в качестве REST-сервера. Я хочу, чтобы ошибка отправлялась обратно как JSON, а не как HTML. Я также определенно не хочу, чтобы моя трассировка стека передавалась моему клиенту.
Я могу отправить JSON в качестве ответа req.json()
, например, используя . что-то вроде req.json({ status: 404, message: 'Uh oh! Can't find something'})
. При желании я могу установить код состояния с помощью req.status()
. Сочетание двух:
req.status(404).json({ status: 404, message: 'Uh oh! Can't find something'});
Это работает как шарм. Тем не менее, я нахожу довольно громоздким вводить каждый раз, когда у меня есть ошибка, и код больше не самодокументируется, как наш next(err)
. Это выглядит слишком похоже на то, как отправляется обычный (т.е. действительный) ответ JSON. Кроме того, любые ошибки, вызванные каноническим подходом, по-прежнему приводят к выводу HTML.
Именно здесь на помощь приходит промежуточное ПО для обработки ошибок Express. В рамках своих маршрутов я определяю:
app.use(function(err, req, res, next) {
console.log('Someone tried to throw an error response');
});
Я также подклассифицирую Error в собственный класс JSONError:
JSONError = function (status, message) {
Error.prototype.constructor.call(this, status + ': ' + message);
this.status = status;
this.message = message;
};
JSONError.prototype = Object.create(Error);
JSONError.prototype.constructor = JSONError;
Теперь, когда я хочу выдать ошибку в коде, я делаю:
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);
Возвращаясь к промежуточному программному обеспечению для пользовательской обработки ошибок, я изменяю его на:
app.use(function(err, req, res, next) {
if (err instanceof JSONError) {
res.status(err.status).json({
status: err.status,
message: err.message
});
} else {
next(err);
}
}
Создание подкласса Error в JSONError важно, поскольку я подозреваю, что Express instanceof Error
проверяет первый параметр, переданный в a, next()
чтобы определить, нужно ли вызывать нормальный обработчик или обработчик ошибок. Я могу удалить instanceof JSONError
проверку и внести незначительные изменения, чтобы непредвиденные ошибки (например, сбой) также возвращали ответ JSON.