Фреймворк Express для серверной разработки на Node.js
Основы работы с Express.js
Как установить Express.js и создать первое приложение?
Основной способ установки и запуска Express.js включает инициализацию проекта npm, установку пакета express и создание файла сервера. Выполняются команды:
npm init -y\nnpm install expressExpress js (express.js)
Создается файл server.js с минимальным кодом:
const express = require('express');\nconst app = express();\nconst port = 3000;\napp.get('/', (req, res) => {\n res.send('Hello World!');\n});\napp.listen(port, () => {\n console.log(`Сервер запущен на порту ${port}`);\n});
Запуск приложения осуществляется командой node server.js. После этого в браузере по адресу http://localhost:3000 отображается сообщение "Hello World!".
Альтернативный подход использует генератор приложений express-generator. Установка:
npx express-generator myapp\ncd myapp\nnpm install\nnpm start
Генератор создает структуру папок, подключает необходимые middleware и предоставляет готовую архитектуру для разработки.
Типичные проблемы: порт 3000 может быть занят другим процессом. В таком случае происходит ошибка EADDRINUSE. Решение: изменить номер порта в коде или завершить процесс, использующий порт. Еще одна ошибка – отсутствие модуля express после установки. Необходимо убедиться, что команда npm install express выполнена в корневой папке проекта, где находится package.json.
Как настроить маршруты с параметрами и обрабатывать разные HTTP методы?
Основная маршрутизация в Express строится на методах объекта app, соответствующих HTTP-глаголам: get, post, put, delete. Параметры пути задаются через двоеточие, а доступ к ним осуществляется через req.params. Пример:
app.get('/users/:userId', (req, res) => {\n const userId = req.params.userId;\n res.send(`Пользователь ${userId}`);\n});\napp.post('/users', (req, res) => {\n const body = req.body;\n res.send('Пользователь создан');\n});
Для получения данных из строки запроса используется req.query. Например: /search?q=express.
Для улучшения организации кода применяется express.Router. Маршруты выносятся в отдельные файлы:
// users.js\nconst router = express.Router();\nrouter.get('/', (req, res) => res.send('Список пользователей'));\nmodule.exports = router;\n\n// server.js\nconst usersRouter = require('./users');\napp.use('/users', usersRouter);
Такой подход упрощает поддержку и тестирование.
Ошибка: порядок middleware имеет значение. Если маршрут с параметром (например, /users/:id) определен после общего маршрута /users, то общий маршрут перехватит запросы. Решение: располагать более специфичные маршруты перед общими. Также часто забывают добавить парсер тела запроса для POST: необходимо подключить express.json() или express.urlencoded().
Как добавить промежуточные обработчики (middleware) для логирования и проверки доступа?
Собственный middleware создается как функция с тремя параметрами (req, res, next) и вызывается через app.use(). Пример логгера:
const logger = (req, res, next) => {\n console.log(`${req.method} ${req.url}`);\n next();\n};\napp.use(logger);
Middleware для проверки аутентификации может прервать цепочку, отправив статус 401:
const auth = (req, res, next) => {\n const token = req.headers['authorization'];\n if (token === 'secret') {\n next();\n } else {\n res.status(401).send('Неавторизованный доступ');\n }\n};\napp.use('/protected', auth);
Популярные сторонние middleware: morgan для логирования, helmet для безопасности, cors для кросс-доменных запросов. Установка и подключение:
npm install morgan helmet cors\nconst morgan = require('morgan');\nconst helmet = require('helmet');\nconst cors = require('cors');\napp.use(morgan('combined'));\napp.use(helmet());\napp.use(cors());
Частая ошибка: middleware не вызывает next(), из-за чего запрос зависает. Необходимо всегда завершать обработку либо вызовом next, либо отправкой ответа. Еще проблема – неправильный порядок: middleware для авторизации должно быть подключено до маршрутов, которые оно защищает.
Как обслуживать статические файлы и шаблоны?
Для статических файлов (CSS, изображения) используется встроенный middleware express.static. Папка с файлами указывается в параметрах:
app.use(express.static('public'));
Файлы из папки public становятся доступны по корневому URL. Для шаблонизации подключается движок, например EJS. Установка и настройка:
npm install ejs\napp.set('view engine', 'ejs');\napp.set('views', './views');
Пример шаблона index.ejs:
<h1><%= title %></h1><p>Добро пожаловать</p>
Вызов в маршруте:
app.get('/page', (req, res) => {\n res.render('index', { title: 'Главная' });\n});
Другие популярные шаблонизаторы: Pug (ранее Jade) и Handlebars. Пример для Pug:
npm install pug\napp.set('view engine', 'pug');\n// Файл index.pug:\n// h1= title\n// p Добро пожаловать
Для Handlebars используется пакет express-handlebars.
Типичные проблемы: неправильный путь к папке с шаблонами – движок не находит файлы. Необходимо убедиться, что директория views существует и содержит файлы с соответствующим расширением. Также ошибка возникает, если не установлен сам пакет шаблонизатора или не указан view engine.
Расширенные примеры использования Express.js
Асинхронные функции в middleware и обработка ошибок
При работе с асинхронными операциями, такими как запросы к базе данных, необходимо корректно передавать ошибки в Express. Для этого используется обертка, которая возвращает middleware, вызывающий next при возникновении ошибки.
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/data', asyncHandler(async (req, res) => {
const result = await fetchSomeData();
res.json(result);
}));
// Если fetchSomeData выбрасывает исключение, оно будет перехвачено и передано в next.
// Результат: при ошибке запроса возвращается статус 500 с сообщением ошибки (если определен middleware для обработки ошибок).
// Пример ответа при успехе: { "data": [...] }
// Пример ответа при ошибке: Статус 500, тело "Internal Server Error" (зависит от реализации error middleware)
Загрузка файлов с multer
Multer – middleware для обработки multipart/form-data. Позволяет сохранять загруженные файлы на диск или в память.
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('file'), (req, res) => {
const file = req.file;
if (!file) {
return res.status(400).send('Файл не загружен');
}
res.send(`Файл ${file.originalname} загружен, размер ${file.size} байт`);
});
// Для загрузки нескольких файлов используется upload.array('files', maxCount)
// При отправке POST запроса с файлом в поле "file" сервер возвращает сообщение о загрузке. // Файл сохраняется в папку uploads с сгенерированным именем. // Пример ответа: "Файл photo.jpg загружен, размер 12345 байт"
Вложенные роутеры и middleware на уровне роутера
Создание иерархии маршрутов с использованием options `mergeParams` для доступа к параметрам родительского роутера.
const parentRouter = express.Router();
const childRouter = express.Router({ mergeParams: true });
// Middleware, применяемый только к дочернему роутеру
childRouter.use((req, res, next) => {
console.log(`Дочерний роутер, userId: ${req.params.userId}`);
next();
});
childRouter.get('/', (req, res) => {
res.send(`Список постов пользователя ${req.params.userId}`);
});
childRouter.get('/:postId', (req, res) => {
res.send(`Пост ${req.params.postId} пользователя ${req.params.userId}`);
});
parentRouter.use('/users/:userId/posts', childRouter);
app.use('/api', parentRouter);
// GET /api/users/123/posts -> Список постов пользователя 123 // GET /api/users/123/posts/456 -> Пост 456 пользователя 123 // В консоли выводится: "Дочерний роутер, userId: 123"