0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>До сих пор мы встречались только со статическими маршрутами. В таких маршрутах нет изменяемых частей - адрес точно совпадает с маршрутом и не меняется. На практике чаще встречаются<strong>динамические маршруты</strong>. Для примера проанализируем адреса курсов на Хекслете:</p>
1
<p>До сих пор мы встречались только со статическими маршрутами. В таких маршрутах нет изменяемых частей - адрес точно совпадает с маршрутом и не меняется. На практике чаще встречаются<strong>динамические маршруты</strong>. Для примера проанализируем адреса курсов на Хекслете:</p>
2
<ul><li><a>https://ru.hexlet.io/courses/php-introduction-to-oop</a></li>
2
<ul><li><a>https://ru.hexlet.io/courses/php-introduction-to-oop</a></li>
3
<li><a>https://ru.hexlet.io/courses/php-object-oriented-design</a></li>
3
<li><a>https://ru.hexlet.io/courses/php-object-oriented-design</a></li>
4
<li><a>https://ru.hexlet.io/courses/js-react</a></li>
4
<li><a>https://ru.hexlet.io/courses/js-react</a></li>
5
</ul><p>В этом уроке вы познакомитесь с динамическими маршрутами и узнаете, как правильно работать с множественными параметрами пути и порядком определения.</p>
5
</ul><p>В этом уроке вы познакомитесь с динамическими маршрутами и узнаете, как правильно работать с множественными параметрами пути и порядком определения.</p>
6
<h2>Динамические маршруты</h2>
6
<h2>Динамические маршруты</h2>
7
<p>Вернемся еще раз к адресам курсов на Хекслете:</p>
7
<p>Вернемся еще раз к адресам курсов на Хекслете:</p>
8
<ul><li><a>https://ru.hexlet.io/courses/php-introduction-to-oop</a></li>
8
<ul><li><a>https://ru.hexlet.io/courses/php-introduction-to-oop</a></li>
9
<li><a>https://ru.hexlet.io/courses/php-object-oriented-design</a></li>
9
<li><a>https://ru.hexlet.io/courses/php-object-oriented-design</a></li>
10
<li><a>https://ru.hexlet.io/courses/js-react</a></li>
10
<li><a>https://ru.hexlet.io/courses/js-react</a></li>
11
</ul><p>Обратите внимание, что в этих адресах прослеживается определенная структура:</p>
11
</ul><p>Обратите внимание, что в этих адресах прослеживается определенная структура:</p>
12
<blockquote><p><em>/courses/<имя курса></em></p>
12
<blockquote><p><em>/courses/<имя курса></em></p>
13
</blockquote><p>Для таких адресов создается ровно один маршрут, в котором изменяемая часть попадает внутрь через контекст как параметр:</p>
13
</blockquote><p>Для таких адресов создается ровно один маршрут, в котором изменяемая часть попадает внутрь через контекст как параметр:</p>
14
<p>В этих примерах мы столкнулись с<strong>динамическими маршрутами</strong>. Они имеют внутри себя изменяемые части, но при этом обработчик у маршрута только один. Например, указанные выше адреса курсов соответствуют одному маршруту, который можно записать так:</p>
14
<p>В этих примерах мы столкнулись с<strong>динамическими маршрутами</strong>. Они имеют внутри себя изменяемые части, но при этом обработчик у маршрута только один. Например, указанные выше адреса курсов соответствуют одному маршруту, который можно записать так:</p>
15
<p>В этом маршруте секция {id} означает, что на это место подставляется конкретный идентификатор курса:</p>
15
<p>В этом маршруте секция {id} означает, что на это место подставляется конкретный идентификатор курса:</p>
16
<p>Имя изменяемой части можно выбирать произвольно - например, вместо {id} можно написать {lala}. Способ записи зависит от конкретного фреймворка. Здесь мы записали имя с обрамляющими фигурными скобками {}, как это принято в Javalin.</p>
16
<p>Имя изменяемой части можно выбирать произвольно - например, вместо {id} можно написать {lala}. Способ записи зависит от конкретного фреймворка. Здесь мы записали имя с обрамляющими фигурными скобками {}, как это принято в Javalin.</p>
17
<h2>Параметры пути</h2>
17
<h2>Параметры пути</h2>
18
<p>Изменяемая часть маршрута в Javalin называется<strong>параметром пути</strong>. В примере выше есть только один такой параметр - это id. Доступ к нему мы получаем через метод ctx.pathParam():</p>
18
<p>Изменяемая часть маршрута в Javalin называется<strong>параметром пути</strong>. В примере выше есть только один такой параметр - это id. Доступ к нему мы получаем через метод ctx.pathParam():</p>
19
<p>Как и в случае с параметрами запроса, возвращаемый тип метода ctx.pathParam() будет равен String. Это подходит не для всех случаев. Поэтому ctx содержит метод, который позволяет автоматически конвертировать данные в нужный тип:</p>
19
<p>Как и в случае с параметрами запроса, возвращаемый тип метода ctx.pathParam() будет равен String. Это подходит не для всех случаев. Поэтому ctx содержит метод, который позволяет автоматически конвертировать данные в нужный тип:</p>
20
<p>Откуда берутся конкретные значения в параметрах пути? Конкретные ссылки формируются на страницах сайта. Для примера можно посмотреть на<a>список постов в блоге Хекслета</a>. На каждый пост есть ссылка, которая формируется в коде как обычная строчка с подстановкой:</p>
20
<p>Откуда берутся конкретные значения в параметрах пути? Конкретные ссылки формируются на страницах сайта. Для примера можно посмотреть на<a>список постов в блоге Хекслета</a>. На каждый пост есть ссылка, которая формируется в коде как обычная строчка с подстановкой:</p>
21
<p>Чтобы пользователям было удобнее, разработчики стараются использовать в адресах не числовые идентификаторы, а человекочитаемые названия. Например, вместо<em>/courses/332</em>показывают<em>/courses/php-mvc</em>. Эту часть адреса называют словом<strong>слаг</strong>(<a>slug</a>). В каждом случае мы используем уникальный слаг, при этом его формат обязан соответствовать требованиям формирования адресов.</p>
21
<p>Чтобы пользователям было удобнее, разработчики стараются использовать в адресах не числовые идентификаторы, а человекочитаемые названия. Например, вместо<em>/courses/332</em>показывают<em>/courses/php-mvc</em>. Эту часть адреса называют словом<strong>слаг</strong>(<a>slug</a>). В каждом случае мы используем уникальный слаг, при этом его формат обязан соответствовать требованиям формирования адресов.</p>
22
<p>Как правило, такие имена содержат символы латинского алфавита с дефисами между словами:</p>
22
<p>Как правило, такие имена содержат символы латинского алфавита с дефисами между словами:</p>
23
<blockquote><p><em>this-that-other-outre-collection</em></p>
23
<blockquote><p><em>this-that-other-outre-collection</em></p>
24
</blockquote><p>Подведем промежуточный итог:</p>
24
</blockquote><p>Подведем промежуточный итог:</p>
25
<ul><li>Понятия "адрес" и "маршрут" обозначают разные вещи</li>
25
<ul><li>Понятия "адрес" и "маршрут" обозначают разные вещи</li>
26
<li>Если маршрут статический, то он всегда совпадает с адресом - например,<em>/about</em></li>
26
<li>Если маршрут статический, то он всегда совпадает с адресом - например,<em>/about</em></li>
27
<li>Если маршрут динамический, то ему могут соответствовать бесконечное число адресов, даже если таких страниц на сайте нет - например,<em>/courses/</em></li>
27
<li>Если маршрут динамический, то ему могут соответствовать бесконечное число адресов, даже если таких страниц на сайте нет - например,<em>/courses/</em></li>
28
</ul><h2>Обработка ошибок</h2>
28
</ul><h2>Обработка ошибок</h2>
29
<p>Большинство фреймворков позволяют задавать любое значение в качестве параметра.<strong>Параметром</strong>считается набор символов, который находится между символами слеша / или после последнего слеша. Фреймворк не знает, какие значения подходят конкретно в нашем случае, поэтому он не может принимать решение за программиста.</p>
29
<p>Большинство фреймворков позволяют задавать любое значение в качестве параметра.<strong>Параметром</strong>считается набор символов, который находится между символами слеша / или после последнего слеша. Фреймворк не знает, какие значения подходят конкретно в нашем случае, поэтому он не может принимать решение за программиста.</p>
30
<p>Предположим, что в нашей базе данных есть 10 курсов c идентификаторами от 1 до 10. Адрес каждого курса формируется так:</p>
30
<p>Предположим, что в нашей базе данных есть 10 курсов c идентификаторами от 1 до 10. Адрес каждого курса формируется так:</p>
31
<blockquote><p><em>/courses/{id}</em></p>
31
<blockquote><p><em>/courses/{id}</em></p>
32
</blockquote><p>В таком случае адрес<em>/courses/1</em>вернет курс с идентификатором<em>1</em>. А вот адрес<em>/courses/11</em>выдаст ошибку 404, потому что такого курса не существует. Чтобы программа сработала именно так, мы должны все правильно реализовать. По идее, код должен выполнять два обязательных действия:</p>
32
</blockquote><p>В таком случае адрес<em>/courses/1</em>вернет курс с идентификатором<em>1</em>. А вот адрес<em>/courses/11</em>выдаст ошибку 404, потому что такого курса не существует. Чтобы программа сработала именно так, мы должны все правильно реализовать. По идее, код должен выполнять два обязательных действия:</p>
33
<ul><li>Проверять наличие данных в базе</li>
33
<ul><li>Проверять наличие данных в базе</li>
34
<li>Выбрасывать исключения в тех случаях, когда данных нет</li>
34
<li>Выбрасывать исключения в тех случаях, когда данных нет</li>
35
</ul><p>При этом исключения должны быть такими, чтобы фреймворк понимал их и обрабатывал как ошибку 404. В Javalin такое исключение работает из коробки. Код в таком случае выглядит примерно так:</p>
35
</ul><p>При этом исключения должны быть такими, чтобы фреймворк понимал их и обрабатывал как ошибку 404. В Javalin такое исключение работает из коробки. Код в таком случае выглядит примерно так:</p>
36
<h2>Множественные параметры пути</h2>
36
<h2>Множественные параметры пути</h2>
37
<p>Иногда в маршруте может быть более одного параметра. Обычно такие маршруты используются для вложенных ресурсов. Именно так работает пример ниже, где уроки вложены в курсы:</p>
37
<p>Иногда в маршруте может быть более одного параметра. Обычно такие маршруты используются для вложенных ресурсов. Именно так работает пример ниже, где уроки вложены в курсы:</p>
38
<h2>Порядок определения</h2>
38
<h2>Порядок определения</h2>
39
<p>В работе с динамическими маршрутами нужно следить за порядком их определения. Иначе мы можем столкнуться с ситуацией, когда одному адресу соответствует несколько маршрутов. Если это произойдет, фреймворк выберет подходящий маршрут, который идет первым по порядку определения.</p>
39
<p>В работе с динамическими маршрутами нужно следить за порядком их определения. Иначе мы можем столкнуться с ситуацией, когда одному адресу соответствует несколько маршрутов. Если это произойдет, фреймворк выберет подходящий маршрут, который идет первым по порядку определения.</p>
40
<p>Посмотрите на этот пример:</p>
40
<p>Посмотрите на этот пример:</p>
41
<p>При таком порядке программа обработает запрос на страницу<em>/courses/build</em>через маршрут<em>/courses/{id}</em>. Это не то, что мы задумывали. Чтобы исправить, поменяем маршруты местами:</p>
41
<p>При таком порядке программа обработает запрос на страницу<em>/courses/build</em>через маршрут<em>/courses/{id}</em>. Это не то, что мы задумывали. Чтобы исправить, поменяем маршруты местами:</p>
42
<p>Ситуация с неверным обработчиком повторится, если мы добавим курс, в которой значение<em>id</em>равно<em>build</em>. Чтобы предотвратить эту проблему, мы просто запрещаем создавать такие<em>id</em>.</p>
42
<p>Ситуация с неверным обработчиком повторится, если мы добавим курс, в которой значение<em>id</em>равно<em>build</em>. Чтобы предотвратить эту проблему, мы просто запрещаем создавать такие<em>id</em>.</p>