Menu

Express Router и маршрутизация: маршруты, параметры

Маршрут в Express это метод, путь и обработчик: app.get('/users/:id', handler). Связанные маршруты группируйте в express.Router() и подключайте под префиксом через app.use('/api', router).

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Маршрутизация в Express сопоставляет HTTP-метод и путь с функцией-обработчиком: app.get('/users', handler) отвечает на GET-запросы к /users, а app.post(), app.put() и app.delete() делают то же самое для других методов. Сегмент, начинающийся с двоеточия, как в /users/:id, это параметр маршрута, значение которого попадает в req.params. Чтобы растущее приложение оставалось в порядке, складывайте связанные маршруты в express.Router() и подключайте его под префиксом через app.use('/api', router).

GET /users/2 отвечает {"id":2,"name":"Linus"}. Параметры маршрута всегда строки, поэтому обработчик сравнивает с Number(req.params.id). Отправьте POST /users с телом {"name": "Grace"} и получите 201, затем DELETE /users/1 и GET /users, чтобы увидеть, как меняется список.

express.Router() с префиксом

Router это мини-приложение со своими маршрутами и middleware. Его пути относительные: маршрут /:id внутри router'а, подключённого на /api/books, отвечает на /api/books/:id. В настоящем проекте каждый router живёт в своём файле и заканчивается строкой module.exports = router.

GET /api/books/1 возвращает {"id":1,"title":"Dune"}. router.route('/:id') записывает путь один раз и цепочкой вешает по обработчику на каждый метод; отправьте PUT /api/books/1, и получите {"updated":"1"}. Поменяйте путь подключения на /v2/books, и тот же router будет отвечать там.

Порядок маршрутов: побеждает первое совпадение

Express пробует маршруты в том порядке, в котором вы их зарегистрировали, и останавливается на первом, который отправил ответ. Маршрут с параметром ловит всё на своей позиции, поэтому до фиксированного пути, зарегистрированного после него, дело не доходит:

GET /users/new отвечает {"route":"/users/:id","id":"new"}: маршрут с параметром зарегистрирован первым и забрал запрос. Перенесите маршрут /users/new выше /users/:id и запустите снова, чтобы получить {"route":"/users/new"}. Затем отправьте GET /books, чтобы попасть в обработчик 404 внизу.

Обработчик 404

На несовпавший запрос Express отвечает своей HTML-страницей Cannot GET /books. Для API обычно нужен JSON, поэтому добавьте app.use() без пути самым последним маршрутом, как в примере выше. Он выполняется, только если все маршруты до него передали запрос дальше. Ошибки, выброшенные обработчиками, это отдельный случай со своим обработчиком на четыре аргумента, о нём статья Обработка ошибок в Express.

Пути маршрутов в Express 5

В Express 5 изменился синтаксис путей, а большинство примеров в интернете написаны для Express 4:

ШаблонExpress 4Express 5
Любой путьapp.get('*', fn)app.get('/*splat', fn)
Необязательный параметр/users/:id?/users{/:id}
Шаблон на параметре/users/:id(\\d+)Не поддерживается; проверяйте значение в обработчике

Одиночная * или ? после параметра выбрасывает TypeError при регистрации маршрута, и приложение не запускается.

Частые ошибки

Обработчик 404 зарегистрирован первым. app.use() без пути совпадает со всем, поэтому каждый запрос получает 404. Держите его в самом низу.

Забыли, что префикс отрезается. Внутри router'а, подключённого на /api, req.path не содержит /api. Когда нужен полный путь, используйте req.originalUrl.

Сравнение параметра с числом. req.params.id === 1 всегда false, потому что req.params.id это строка '1'.

Часто задаваемые вопросы

Чем app.use отличается от app.get?

app.get('/users', fn) вызывает fn только для GET-запросов, путь которых ровно /users. app.use('/users', fn) срабатывает для любого метода и любого пути, который начинается с /users, поэтому через него подключают router'ы и middleware.

Как сделать маршруты Express чувствительными к регистру?

По умолчанию маршруты не учитывают регистр, поэтому /Users совпадает с /users. Это отключается через app.set('case sensitive routing', true) или для отдельного router'а через express.Router({ caseSensitive: true }).

Почему req.params пустой во вложенном router'е?

Router видит только параметры своих путей. Если он подключён на /users/:userId/posts, создавайте его через express.Router({ mergeParams: true }), чтобы req.params.userId из родительского пути был доступен.

Стоит ли держать все маршруты в одном файле?

Нет, когда их больше нескольких. Создайте по router'у на ресурс (routes/users.js, routes/orders.js), экспортируйте его через module.exports = router и подключите каждый в главном файле через app.use().

Иллюстрация языков программирования Coddy

Учитесь программировать с Coddy

Начать