Любое приложение на Express состоит минимум из трёх элементов:
- Создание объекта приложения;
- Определение обработчиков маршрутов;
- Запуск приложения на определённом порту.
import Express from 'express';
const app = new Express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Example app listening on port 3000!');
});
С первым и последним пунктом всё более-менее понятно, а вот обработчики — это уже интересно. Напомню, что микрофреймворки строятся по схеме: http verb
+ path
+ callback
. В случае Express глагол определяется функцией, которая вызывается на объекте app
. Например если мы хотим определить маршрут GET /
, то для этого необходимо вызывать функцию get
и первым параметром передать в неё строку /
. Таким же образом нужно поступать для любого другого маршрута. Вот список глаголов, которые нас будут интересовать в процессе этого курса:
// И - Идемпотентный
app.head // И
app.get // И
app.post
app.delete // И
// Полное обновление
app.put // И
// Частичное обновление
app.patch
Семантика http
подразумевает, что глаголы get, delete, head и put, являются идемпотентными. Это должно обеспечиваться программистом, который реализует обработчики.
Динамические маршруты
Самая главная и мощная функциональность системы роутинга — это работа с динамическими маршрутами.
Route path: /users/:userId/books/:id
Request URL: http://localhost:3000/users/34/books/8989
req.params: { "userId": "34", "id": "8989" }
app.get('/users/:userId/books/:id', (req, res) => {
const { userId, id } = req.params;
});
Для динамических частей используется заполнитель :name
, который состоит из двоеточия и произвольного имени. Express производит сопоставление актуального запроса со всеми шаблонами в порядке определения и выполняет соответствующие обработчики. Все динамические части маршрута попадают в объект req.params
.
Кроме этого, Express позволяет использовать регулярные выражения прямо в шаблоне:
// abcd, abxcd, abRANDOMcd, ab123cd
app.get('/ab*cd', (req, res) => {
res.send('ab*cd');
});
Честно говоря, за свой многолетний опыт я не припомню ситуацию, когда мне это могло понадобиться. Всегда, если у вас есть возможность управлять урлами, можно сделать хороший вариант на одних плейсхолдерах (заполнителях), без прямого использования регулярных выражений.
Именованные маршруты
В приложении есть различные роуты. Например, у авторизации может стоять роут /signup
. Когда мы отдаем с сервера HTML, то этот адрес мы прописываем дважды:
- В роуте на сервере
app.post('/signup', signupHandler)
- В HTML
<a href="/signup">Зарегистрироваться</a>
Про такое решение обычно говорят «хардкодить ссылки» или «магические строки». Если по какой-то причине мы решим переименовать этот роут, например, в /register
, то нам надо поменять его и в роутах, и в HTML. В реальных приложениях роутов много, они состоят из нескольких частей, и переименование — не такая уж редкость.
Чтобы облегчить работу с именованием роутов, можно выделить их в отдельную константу и переиспользовать ее:
// Можно выделить отдельный модуль и импортировать из него объект с роутами:
const routes = { auth: '/signin', register: '/signup' }
// Переиспользуем роут
app.post(routes.register, (req, res) => {
// ...
});
// Где-то в шаблоне
// <a href=`"${routes.register}"`>Зарегистрироваться</a>`.
Так мы сделаем именованные роуты. Теперь все адреса записаны в одном месте и их легко поменять. Весь остальной код будет использовать эти имена.
Есть другой подход в создании именованных роутеров — с помощью специальных библиотек. Например, named-routes
:
import Express from 'express';
// Импортируем библиотеку
import Router from 'named-routes';
const app = new Express();
// Создаем объект роутера
const router = new Router();
// Подключаем в приложение
router.extendExpress(app);
router.registerAppHelpers(app);
app.get('/admin/users/:id', 'admin.user', (req, res, next) => {
// ...
// Создаем маршрут
const path = app.namedRoutes.build('admin.user', { id: 2 }); // /admin/users/2
// Имя текущего маршрута будет находиться в req.route.name
});
В примере выше был создан именованный маршрут admin.user
. Теперь, где-то в другом обработчике можно строить ссылки используя имя маршрута:
app.get('/users', 'users', (req, res, next) => {
const path = app.namedRoutes.build('admin.user', { id: 2 }); // /admin/users/2
// path содержит путь с которым можно сделать что-то полезное
});
Преимущества:
- Если удалится маршрут, то при генерации ссылки мы получим исключение, и тесты его поймают
- Если изменится маршрут, то везде будут автоматически подставлены новые адреса
Чтобы сделать именованный роутинг действительно полезным и удобным, в других языках фреймворки предоставляют готовые решения, но в Express из коробки такой возможности нет. Поэтому в упражнениях мы продолжим хардкодить ссылки, но попробовать именованный роутинг можно будет в проекте.
Request/Response
Каждый определяемый обработчик принимает на вход два параметра: req
и res
. В этом смысле, всё очень похоже на ситуацию с http
модулем с той лишь разницей, что там подобный обработчик один, а здесь на каждый маршрут свой. Request
и Response
предоставляют упрощенный интерфейс. Больше нет необходимости, по крайней мере в простых случаях, пользоваться ими как eventEmitter
. На каждую задачу эти объекты предоставляют метод или свойство.
// GET /users?page=3
app.get('/users', (req, res) => {
console.log(req.query); // { page: 3 }
res.status(200);
res.send(users);
// res.json(users);
});
Пройдёмся по базовым возможностям:
req.query
готовый к обработкеquery string
, другими словами вам не придётся парсить эти параметры, они уже поступают в виде объекта;- Установка статуса работает через метод
res.status(code)
; - Отправка данных выполняется функцией
res.send(data)
. Все необходимые заголовки выставляются автоматически; - Если необходимо отдавать данные в виде
json
, то Express позволяет это делать без использования дополнительных механизмов. Всё что нужно сделать это вызвать методres.json(data)
, и данные будут автоматически сериализованы и отправлены клиенту.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты