Зарегистрируйтесь, чтобы продолжить обучение

Динамические маршруты JS: Веб-разработка

До сих пор мы встречались только со статическими маршрутами. В таких маршрутах нет изменяемых частей — адрес точно совпадает с маршрутом и не меняется. На практике чаще встречаются динамические маршруты. Для примера проанализируем адреса курсов на Хекслете:

В этом уроке вы познакомитесь с динамическими маршрутами и узнаете, как правильно работать с множественными параметрами пути и порядком определения.

Динамические маршруты

Вернемся еще раз к адресам курсов на Хекслете:

Обратите внимание, что в этих адресах прослеживается определенная структура:

/courses/<имя курса>

Для таких адресов создается ровно один маршрут, в котором изменяемая часть попадает внутрь через контекст как параметр:

import fastify from 'fastify'

const app = fastify()
const port = 3000

app.get('/courses/:id', (req, res) => {
  res.send(`Course ID: ${req.params.id}`)
})

app.get('/users/:id', (req, res) => {
  res.send(`User ID: ${req.params.id}`)
})

app.listen({ port }, () => {
  console.log(`Example app listening on port ${port}`)
})

В этих примерах мы столкнулись с динамическими маршрутами. Они имеют внутри себя изменяемые части, но при этом обработчик у маршрута только один. Например, указанные выше адреса курсов соответствуют одному маршруту, который можно записать так:

`/courses/${id}`

В этом маршруте секция :id означает, что на это место подставляется конкретный идентификатор курса:

curl localhost:3000/courses/132
Course id: 132

curl localhost:3000/courses/php-oop
Course id: php-oop

Имя изменяемой части можно выбирать произвольно — например, вместо id можно написать lala. Способ записи зависит от конкретного фреймворка. Здесь мы записали имя с дополнительным двоеточием :, как это принято в Fastify.

Параметры пути

Изменяемая часть маршрута в Fastify называется параметром пути. В примере выше есть только один такой параметр — это id. Доступ к нему мы получаем через свойство req.params.id:

const id = req.params.id

Как и в случае с параметрами запроса, параметр пути будет строкой.

Откуда берутся конкретные значения в параметрах пути? Конкретные ссылки формируются на страницах сайта. Для примера можно посмотреть на список постов в блоге Хекслета. На каждый пост есть ссылка, которая формируется в коде как обычная строчка с подстановкой:

const url = `/blog/posts/${post.id}`

Чтобы пользователям было удобнее, разработчики стараются использовать в адресах не числовые идентификаторы, а человекочитаемые названия. Например, вместо /courses/332 показывают /courses/php-mvc. Эту часть адреса называют словом слаг (slug). В каждом случае мы используем уникальный слаг, при этом его формат обязан соответствовать требованиям формирования адресов.

Как правило, такие имена содержат символы латинского алфавита с дефисами между словами:

this-that-other-outre-collection

Подведем промежуточный итог:

  • Понятия «адрес» и «маршрут» обозначают разные вещи
  • Если маршрут статический, то он всегда совпадает с адресом — например, /about
  • Если маршрут динамический, то ему могут соответствовать бесконечное число адресов, даже если таких страниц на сайте нет — например, /courses/:id

Обработка ошибок

Большинство фреймворков позволяют задавать любое значение в качестве параметра. Параметром считается набор символов, который находится между символами слеша / или после последнего слеша. Фреймворк не знает, какие значения подходят конкретно в нашем случае, поэтому он не может принимать решение за программиста.

Предположим, что в нашей базе данных есть 10 курсов c идентификаторами от 1 до 10. Адрес каждого курса формируется так:

/courses/:id

В таком случае адрес /courses/1 вернет курс с идентификатором 1. А вот адрес /courses/11 выдаст ошибку 404, потому что такого курса не существует. Чтобы программа сработала именно так, мы должны все правильно реализовать. По идее, код должен выполнять два обязательных действия:

  • Проверять наличие данных в базе
  • Возвращать ответ с кодом 404 в тех случаях, когда данных нет

Код в таком случае выглядит примерно так:

const state = {
  users: [
    {
      id: 1,
      name: 'user',
    },
  ],
}

app.get('/users/:id', (req, res) => {
  const { id } = req.params
  const user = state.users.find(user => user.id === parseInt(id))
  if (!user) {
    res.code(404).send({ message: 'User not found' })
  }
  else {
    res.send(user)
  }
})

Множественные параметры пути

Иногда в маршруте может быть более одного параметра. Обычно такие маршруты используются для вложенных ресурсов. Именно так работает пример ниже, где уроки вложены в курсы:

import fastify from 'fastify'

const app = fastify()
const port = 3000

app.get('/courses/:courseId/lessons/:id', (req, res) => {
  res.send(`Course ID: ${req.params.courseId}; Lesson ID: ${req.params.id}`)
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

Порядок определения

В работе с динамическими маршрутами может возникнуть необходимость следить за порядком их определения. Иначе мы можем столкнуться с ситуацией, когда одному адресу соответствует несколько маршрутов. Если это произойдет, фреймворк может выбрать маршрут, который идет первым по порядку определения.

Посмотрите на этот пример:

app.get('/courses/:id', (req, res) => {
  res.send(`Course ID: ${req.params.id}`)
})

app.get('/courses/new', (req, res) => {
  res.send('Course build')
})

В Fastify при таком порядке программа обработает запрос на страницу /courses/new через корректный маршрут /courses/new. Иными словами, в Fastify проблемы нет. Однако в других фреймворках это может работать иначе, и обработка маршрута в таком случае пойдет через /course/:id. Чтобы это исправить, нужно поменять маршруты местами:

app.get('/courses/new', (req, res) => {
  res.send('Course build')
})

app.get('/courses/:id', (req, res) => {
  res.send(`Course ID: ${req.params.id}`)
})

Ситуация с неверным обработчиком повторится, если мы добавим курс, в котором значение id равно new. Чтобы предотвратить эту проблему, мы просто запрещаем создавать такие id.


Самостоятельная работа

  1. Выполните все шаги из этого урока на своем компьютере
  2. По аналогии с примерами из урока, добавьте в приложение обработчик, который будет обрабатывать запросы по пути users/{id}/post/{postId}
  3. Запустите приложение и убедитесь, что все работает
  4. Залейте изменения на GitHub

Дополнительные материалы

  1. Человекопонятный URL

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff