HTML Diff
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/&lt;имя курса&gt;</em></p>
12 <blockquote><p><em>/courses/&lt;имя курса&gt;</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>