Улучшите свои приложения Node.js с помощью настраиваемых кодов ошибок.

В этой статье вы узнаете, как перезаписать промежуточное программное обеспечение для обработки ошибок по умолчанию, создав собственную функцию промежуточного программного обеспечения, которая будет централизовать ошибки, выдаваемые приложением, и выполнять соответствующую логику в соответствии с контекстом.
Встроенное промежуточное ПО для обработки ошибок
Инфраструктура Express поставляется со встроенным промежуточным программным обеспечением для обработки ошибок, которое отвечает за обнаружение любых ошибок во время выполнения, с которыми может столкнуться приложение, что предотвращает его сбой.
По умолчанию он регистрирует ошибку в консоли и отвечает клиенту HTTP 500 Internal Server Error, содержащим сообщение об ошибке, а также трассировку стека (если не в рабочей среде) в формате HTML.
{
status: 500,
statusText: 'Internal Server Error',
data: '<!DOCTYPE html>\n' + '<html lang="en">\n' + '<head>\n' + '<meta
charset="utf-8">\n' + '<title>Error</title>\n' + '</head>\n' + '<body>\n' +
'<pre>Error: BROKEN<br> at /Users/rludosanu/Projects/test/
test.js:5:9<br> at Layer.handle [as handle_request] (/Users/
rludosanu/Projects/test/node_modules/express/lib/router/layer.js:95:5)<br>
at next (/Users/rludosanu/Projects/test/node_modules/express/
lib/router/route.js:144:13)<br> at Route.dispatch (/Users/
rludosanu/Projects/test/node_modules/express/lib/router/route.js:114:3)<br>
at Layer.handle [as handle_request] (/Users/rludosanu/
Projects/test/node_modules/express/lib/router/layer.js:95:5)<br>
at /Users/rludosanu/Projects/test/node_modules/express/lib/router/
index.js:284:15<br> at Function.process_params (/Users/
rludosanu/Projects/test/node_modules/express/lib/router/
index.js:346:12)<br> at next (/Users/rludosanu/Projects/test/
node_modules/express/lib/router/index.js:280:10)<br> at
expressInit (/Users/rludosanu/Projects/test/node_modules/express/lib/
middleware/init.js:40:5)<br> at Layer.handle [as
handle_request] (/Users/rludosanu/Projects/test/node_modules/express/lib/
router/layer.js:95:5)</pre>\n' + '</body>\n' + '</html>\n'
}
Хотя это полезно, когда приложение находится на ранней стадии разработки, этот обработчик по умолчанию показывает свои ограничения, когда речь идет о реальных API, которые должны быть способны обрабатывать несколько типов ошибок разными способами, например, отвечать клиенту с другим статусом. коды или записи этих ошибок в лог-файлы.
Базовое промежуточное ПО для обработки ошибок
Создание промежуточного программного обеспечения
В Express функция промежуточного программного обеспечения для обработки ошибок ничем не отличается от любого другого промежуточного программного обеспечения, за исключением того, что она принимает 4 аргумента вместо 3:
// File: error-handler.js
function errorHandler(err, req, res, next) {
console.error(err);
res.sendStatus(500);
}
module.exports = errorHandler;
Установка промежуточного программного обеспечения
Это промежуточное ПО должно быть смонтировано в приложении через метод app.use() в самом низу стека вызовов, после всех остальных объявлений:
// File: app.js
const express = require('express');
const app = express();
const errorHandler = require('./error-handler');
app.get('/health', (req, res) => { /* ... */ });
app.post('/login', (req, res) => { /* ... */ });
app.post('/signup', (req, res) => { /* ... */ });
app.use(errorHandler);
app.listen(3000);
Вызов промежуточного программного обеспечения
Чтобы передать ошибки этому промежуточному ПО, все, что нам нужно сделать, это вызвать параметр next() контроллера маршрута, который передаст запрос следующему компоненту стека:
// File: app.js
const express = require('express');
const app = express();
const errorHandler = require('./error-handler');
app.post('/login', (req, res, next) => {
next(new Error('FAILED'));
});
app.use(errorHandler);
app.listen(3000);
Тестирование промежуточного программного обеспечения
Вы можете попробовать сами, запустив это приложение в новом окне терминала:
$ node app.js
И отправив следующий запрос на маршрут POST /login с помощью cURL:
$ curl -v -X POST 127.0.0.1:3000/login
Выдача пользовательских ошибок
Создание нового класса ошибок
Чтобы иметь возможность выдавать пользовательские ошибки, мы собираемся создать новый класс ошибок с именем CustomError, который наследуется от встроенного класса Error, где конструктор будет принимать в качестве аргумента константу code, представляющую тип пользовательской ошибки, вызванной приложением. . Например, INVALID_EMAIL для недействительного адреса электронной почты или USER_NOT_FOUND для пары адрес электронной почты и пароль, которой нет в базе данных.
// File: custom-error.js
class CustomError extends Error {
constructor(code) {
super();
this.code = code;
}
}
module.exports = CustomError;
Определение пользовательских кодов ошибок
Теперь мы собираемся создать объект, который сопоставляет пользовательские коды ошибок с действительными статусами HTTP и удобочитаемыми сообщениями.
// File: error-codes.js
module.exports = {
INVALID_EMAIL_OR_PASSWORD: {
statusCode: 400, // Bad Request
message: 'Invalid email address or password',
},
USER_NOT_FOUND: {
statusCode: 404, // Not Found
message: 'User not found',
},
INTERNAL_ERROR: {
statusCode: 500, // Internal Server Error
message: 'Internal Server Error',
},
};
Улучшение промежуточного ПО для обработки ошибок
Теперь, когда у нас есть класс CustomError и список пользовательских ошибок, мы можем улучшить наше промежуточное ПО для обработки ошибок, чтобы оно:
- Анализирует ошибку
code, содержащуюся в объектеerr. - Сопоставляет ошибку
codeсо списком пользовательских ошибок. - Отправляет ответ клиенту с соответствующим кодом состояния и сообщением об ошибке или стандартной внутренней ошибкой сервера HTTP 500.
// File: error-handler.js
const errorCodes = require('./error-codes');
function errorHandler(err, req, res, next) {
const code = (err && err.code) || null;
const error = errorCodes[code] || errorCodes['INTERNAL_ERROR'];
return res
.status(error.statusCode)
.json({ message: error.message });
};
module.exports = errorHandler;
Полный пример
Чтобы проиллюстрировать это, мы собираемся создать простое приложение, реализующее маршрут POST /login, позволяющий пользователям аутентифицироваться с помощью адреса электронной почты и пароля.
Приложение
Когда запрос достигает маршрута POST /login, его полезная нагрузка преобразуется в объект JavaScript с помощью встроенного промежуточного программного обеспечения express.json() и перенаправляется в функцию loginController().
// File: app.js
const express = require('express');
const app = express();
const loginController = require('./login-controller');
const errorHandler = require('./error-handler.js');
app.post('/login', express.json(), loginController);
app.use(errorHandler);
app.listen(3000);
Контроллер
loginController() вызывает loginService() с проанализированными данными, содержащимися в свойстве req.body. Если аутентификация прошла успешно, он отвечает клиенту HTTP 200, содержащим строку токена доступа. В противном случае он перенаправляет любую возникшую ошибку промежуточному программному обеспечению для обработки ошибок с помощью функции next().
// File: login-controller.js
const loginService = require('./login-service');
function loginController(req, res, next) {
try {
const token = loginService(req.body);
res.json({ token });
} catch(error) {
next(error);
}
}
module.exports = loginController;
Обслуживание
loginService() проверяет данные, отправленные loginController(), и выдает CustomError, если email или password недействительны или не соответствуют ожидаемым значениям. В противном случае возвращается строка, представляющая токен доступа.
// File: login-service.js
const CustomError = require('./custom-error');
function loginService(data) {
const { email, password } = data;
const token = 'access_token';
if (!email || !password) {
throw new CustomError('INVALID_EMAIL_OR_PASSWORD');
}
if (email !== '[email protected]' || password !== 'helloworld') {
throw new CustomError('USER_NOT_FOUND');
}
return token;
}
module.exports = loginService;
Тестирование приложения
Теперь вы можете запустить это приложение в новом окне терминала:
$ node app.js
И убедитесь, что мы получаем HTTP 200, содержащий токен доступа, когда адрес электронной почты и пароль совпадают:
$ curl -v -X POST \
-H 'Content-Type: application/json' \
-d '{"email":"[email protected]","password":"helloworld"}' \
127.0.0.1:3000/login
Что мы можем получить HTTP 404, когда электронная почта не совпадает:
$ curl -v -X POST \
-H 'Content-Type: application/json' \
-d '{"email":"[email protected]","password":"helloworld"}' \
127.0.0.1:3000/login
То, что мы можем HTTP 400, когда электронная почта отсутствует:
$ curl -v -X POST \
-H 'Content-Type: application/json' \
-d '{"email":"","password":"helloworld"}' \
127.0.0.1:3000/login
Поднимите свои навыки бэкенда и Node.js на новый уровень на learnbackend.dev