Express / 38 минут
Обработка ошибок Express
Направляйте ошибки в централизованный обработчик.
- Направляйте ошибки в централизованный обработчик.
- Применить тему в задаче «Обработка ошибок Express» и объяснить граничные случаи.
Около 8 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.
Ожидаемый отказ и неожиданная ошибка
Неверный пользовательский ввод, отсутствие записи и отказ в доступе — ожидаемые ветви публичного контракта. Поломка соединения с БД или ошибка реализации — другая категория. Их полезно различать, чтобы клиент получил подходящий статус, а разработчик — диагностическую информацию. Возвращать одну 500 для всего неудобно так же, как скрывать все отказы за 200.
Централизованный error middleware помогает сделать форму неожиданной ошибки одинаковой. Он не должен заменять осмысленную валидацию и авторизацию, но позволяет не копировать безопасное форматирование в каждый handler. Главное — правильно направить ошибку в цепочку и зарегистрировать обработчик после слоёв, которые могут её породить.
Четырёхаргументная сигнатура
Error middleware имеет параметры err, req, res, next. Даже если часть не используется внутри, форма функции важна для распознавания её назначения. Обычная функция из трёх параметров не станет error handler только из-за имени файла errors.js. Порядок регистрации также остаётся значимым.
app.get('/fail', () => {
throw new Error('Внутренняя подробность');
});
app.use((err, req, res, next) => {
if (res.headersSent) return next(err);
res.status(500).json({ error: 'Ошибка сервера' });
});В этом примере клиент не получает err.stack и исходное сообщение. Подробности можно записать в контролируемый серверный лог с идентификатором запроса, избегая паролей, cookies и полного чувствительного body. Безопасный публичный ответ и полезная диагностика не противоречат друг другу, если у них разные каналы.
next(err) и синхронные исключения
Синхронное исключение внутри обработчика Express направляет в механизм ошибок. В callback внешнего API нужно явно связать ошибку с Express, если библиотека не возвращает подходящий Promise. Вызов next(err) переключает обработку в ошибочную ветвь. Не вызывайте затем успешную отправку ответа в той же ветви.
app.get('/manual', (req, res, next) => {
const error = new Error('Учебный отказ');
return next(error);
});Не используйте next как универсальный способ продолжить любой код после отказа. Если ресурс отсутствует и вы уже вернули 404, на этом текущая обработка должна закончиться. Двойное завершение ответа часто возникает из-за отсутствующего return или из-за оторванной асинхронной работы, которая позже пытается отправить ещё один результат.
Promise и Express 5
В Express 5 отклонённый Promise, возвращённый обработчиком или middleware, передаётся в обработку ошибок. Это упрощает async/await по сравнению со старыми обёртками из примеров Express 4. Однако обычный detached Promise, не возвращённый и не ожидаемый цепочкой, остаётся отдельной ответственностью приложения.
app.get('/async-fail', async () => {
await Promise.resolve();
throw new Error('Ошибка после ожидания');
});Не делайте все ошибки одинаково ожидаемыми. Например, потерянный файл настроек сервера не всегда следует превращать в «пользователь не найден». Ошибка должна иметь владельца и понятный способ интерпретации. Для внешней операции заранее определите timeout, возможность повтора и действия при частично завершённом сценарии.
Заголовки уже отправлены и 404
После отправки заголовков нельзя начать новый обычный JSON-ответ с другим статусом. Если ошибка возникла в процессе потоковой передачи или после начала ответа, проверяйте res.headersSent и делегируйте подходящему завершающему механизму. Попытка res.status(500).json поверх уже отправленного ответа только добавит новую ошибку.
app.use((_req, res) => res.status(404).json({ error: 'not_found' }));
app.use((err, req, res, next) => {
if (res.headersSent) return next(err);
res.status(500).json({ error: 'internal' });
});Отсутствие подходящего маршрута не обязано быть исключением. Завершающий обычный middleware удобно отвечает 404, а четырёхаргументный — обрабатывает ошибочную ветвь. Согласованная форма ответа помогает клиенту различать эти ситуации без анализа произвольного HTML или текста стека.
Практика безопасной ошибки
Задача требует настоящего GET /fail, который бросает Error, и error middleware, возвращающего 500 с фиксированным безопасным JSON. solve делает HTTP-запрос и извлекает статус и тело. Если просто вернуть нужный объект без запроса, механизм обработки не будет проверен и образовательный смысл упражнения пропадёт.
После решения проверьте, что приватная строка исходного Error не попала в body. Затем добавьте async-отказ и обычный неизвестный маршрут, чтобы увидеть разные пути цепочки. Для итогового проекта опишите небольшой набор публичных кодов ошибок и отдельный журнал диагностики. Это сделает сервер удобнее и для клиента, и для сопровождения.