«1. Обзор

Маршрутизация — это общая концепция, которая используется в большинстве сред веб-разработки, включая Spring MVC.

Маршрут — это шаблон URL, сопоставленный с обработчиком. Обработчик может быть физическим файлом, например загружаемым ресурсом в веб-приложении, или классом, обрабатывающим запрос, например контроллером в приложении MVC.

В этом руководстве мы рассмотрим аспект маршрутизации при разработке веб-приложений с помощью Play Framework.

2. Настройка

Во-первых, нам нужно создать приложение Java Play. Подробная информация о том, как настроить Play Framework на компьютере, доступна в нашей вводной статье.

К концу установки у нас должно быть работающее приложение Play, к которому мы можем получить доступ из браузера.

3. HTTP-маршрутизация

Так как же Play узнает, к какому контроллеру обращаться каждый раз, когда мы отправляем HTTP-запрос? Ответ на этот вопрос лежит в конфигурационном файле app/conf/routes.

Маршрутизатор Play преобразует HTTP-запросы в вызовы действий. HTTP-запросы считаются событиями в архитектуре MVC, и маршрутизатор реагирует на них, консультируясь с файлом маршрутов, для какого контроллера и какое действие в этом контроллере выполнять.

Каждое из этих событий предоставляет маршрутизатору два параметра: путь запроса со строкой запроса и HTTP-метод запроса.

4. Базовая маршрутизация с помощью Play

Чтобы маршрутизатор мог выполнять свою работу, в файле conf/routes должны быть определены сопоставления методов HTTP и шаблонов URI с соответствующими действиями контроллера:

GET     /     controllers.HomeController.index
GET     /     assets/*file controllers.Assets.versioned(path="/public", file: Asset)

Все файлы маршрутов также должны сопоставьте статические ресурсы в папке play-routing/public, доступной клиенту в конечной точке /assets. Обратите внимание на синтаксис определения маршрутов HTTP и действие контроллера пространства шаблонов URI пространства методов HTTP.

5. Шаблоны URI

В этом разделе мы немного расскажем о шаблонах URI.

5.1. Статические шаблоны URI

Первые три приведенных выше шаблона URI являются статическими. Это означает, что сопоставление URL-адресов с ресурсами происходит без какой-либо дальнейшей обработки в действиях контроллера.

Пока вызывается метод контроллера, он возвращает статический ресурс, содержимое которого определяется до запроса.

5.2. Динамические шаблоны URI

Последний шаблон URI выше является динамическим. Это означает, что действию контроллера, обслуживающему запрос на эти URI, требуется некоторая информация из запроса для определения ответа. В приведенном выше случае он ожидает имя файла.

Обычная последовательность событий такова, что маршрутизатор получает событие, выбирает путь из URL-адреса, декодирует его сегменты и передает их контроллеру.

Параметры пути и запроса затем вводятся в действие контроллера в качестве параметров. Мы продемонстрируем это на примере в следующих разделах.

6. Расширенная маршрутизация с помощью Play

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

6.1. Параметры простого пути

Параметры простого пути — это безымянные параметры в URL-адресе запроса, которые появляются после хоста и порта и анализируются в порядке появления.

Внутри play-routing/app/HomeController.java давайте создадим новое действие:

public Result greet(String name) {
    return ok("Hello " + name);
}

Мы хотим иметь возможность выбрать параметр пути из URL-адреса запроса и сопоставить его с именем переменной.

Маршрутизатор получит эти значения из конфигурации маршрута.

Итак, давайте откроем play-routing/conf/routes и создадим сопоставление для этого нового действия:

GET     /greet/:name     controllers.HomeController.greet(name: String)

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

Теперь давайте загрузим http://locahost:9000/greet/john в браузере, и нас будут приветствовать по имени:

Hello john

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

Давайте оживим нашу конечную точку /greet информацией о возрасте.

Вернемся к действию приветствия HomeController, мы изменим его на:

public Result greet(String name, int age) {
    return ok("Hello " + name + ", you are " + age + " years old");
}

И маршрут к:

GET     /greet/:name/:age               controllers.HomeController.greet(name: String, age: Integer)

«

«Обратите также внимание на синтаксис Scala для объявления переменной age: Integer. В Java мы будем использовать синтаксис Integer age. Play Framework построен на Scala. Следовательно, существует много синтаксиса scala.

Hello john, you are 26 years old

Давайте загрузим http://localhost:9000/greet/john/26:

6.2. Подстановочные знаки в параметрах пути

GET     /assets/*file  controllers.Assets.versioned(path="/public", file: Asset)

В нашем файле конфигурации маршрутов последнее сопоставление:

Мы используем подстановочный знак в динамической части пути. Мы говорим Play, что любое значение, заменяющее *file в фактическом запросе, должно анализироваться целиком, а не декодироваться, как в других случаях параметров пути.

В этом примере контроллер встроенный, Assets, который позволяет клиенту скачивать файлы из папки play-routing/public. Когда мы загружаем http://localhost:9000/assets/images/favicon.png, мы должны увидеть изображение значка Play в браузере, поскольку оно присутствует в папке /public/images.

public Result introduceMe(String data) {
    String[] clientData = data.split(",");
    return ok("Your name is " + clientData[0] + ", you are " + clientData[1] + " years old");
}

Давайте создадим наш собственный пример действия в HomeController.java:

Обратите внимание, что в этом действии мы получаем один строковый параметр и применяем нашу логику для его декодирования. В этом случае логика состоит в том, чтобы разбить строку с разделителями-запятыми на массив. Раньше мы зависели от маршрутизатора, который расшифровывал эти данные.

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

GET   /*data   controllers.HomeController.introduceMe(data)

Создадим маршрут к этому действию:

Your name is john, you are 26 years old

Теперь загрузим URL http://localhost:9000/john,26. Это напечатает:

6.3. Регулярные выражения в параметрах пути

public Result squareMe(Long num) {
    return ok(num + " Squared is " + (num * num));
}

Как и подстановочные знаки, мы можем использовать регулярные выражения для динамической части. Давайте добавим действие, которое получает число и возвращает его квадрат:

GET   /square/$num<[0-9]+>   controllers.HomeController.squareMe(num:Long)

Теперь мы добавим его маршрут:

Давайте поместим этот маршрут ниже маршрута IntroductionMe, чтобы представить новую концепцию. С этой конфигурацией маршрутизации мы можем обрабатывать только маршруты, где часть регулярного выражения является положительным целым числом.

Теперь, если мы разместили маршрут, как указано в предыдущем абзаце, и загружаем http://localhost:9000/square/2, нас должно приветствовать ArrayIndexOutOfBoundsException:

Если мы проверим журналы ошибок в консоли сервера, мы поймем, что вызов действия фактически был выполнен для действия IntroductionMe, а не для действия SquareMe. Как было сказано ранее о подстановочных знаках, мы сами по себе и не проверяли входящие данные.

Вместо строки, разделенной запятыми, метод IntroductionMe был вызван со строкой «square/2». Следовательно, после его разбиения мы получили массив единичного размера. Попытка достичь индекса 1 вызвала исключение.

Естественно, мы ожидаем, что вызов будет перенаправлен на метод squareMe. Почему он был направлен, чтобы представить Меня? Причина в функции Play, которую мы рассмотрим далее, под названием «Приоритет маршрутизации».

7. Приоритет маршрутизации

Если есть конфликт между маршрутами, как между SquareMe и IntroductionMe, то Play выбирает первый маршрут в порядке объявления.

Почему возникает конфликт? Из-за подстановочного пути контекста /*данные соответствуют любому URL-адресу запроса, кроме базового пути/. Таким образом, каждый маршрут, шаблон URI которого использует подстановочные знаки, должен отображаться последним по порядку.

2 Squared is 4

Теперь давайте изменим порядок объявления маршрутов таким образом, чтобы маршрут IntroductionMe шел после SquareMe и перезагрузил:

Чтобы проверить силу регулярных выражений в маршруте, попробуйте загрузить http://locahost:9000/ Square/-1 маршрутизатор не сможет сопоставить маршрут SquareMe. Вместо этого он будет соответствовать вводитьMe, и мы снова получим исключение ArrayIndexOutOfBoundsException.

Это связано с тем, что указанное регулярное выражение не соответствует -1, равно как и любой буквенный символ.

8. Параметры

До этого момента мы рассмотрели синтаксис объявления типов параметров в файле маршрутов.

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

8.1. Параметры с фиксированными значениями

«Иногда мы хотим использовать фиксированное значение для параметра. Это наш способ сказать Play использовать предоставленный параметр пути или, если контекстом запроса является путь /, то использовать определенное фиксированное значение.

Другой способ взглянуть на это — иметь две конечные точки или контекстные пути, ведущие к одному и тому же действию контроллера — с одной конечной точкой, требующей параметр из URL-адреса запроса, и по умолчанию для другой, если указанный параметр отсутствует.

public Result writer() {
    return ok("Routing in Play by Baeldung");
}

Чтобы продемонстрировать это, давайте добавим действие Writer() в HomeController:

Routing in Play by Baeldung

Предполагая, что мы не хотим, чтобы наш API всегда возвращал строку:

Мы хотим управлять им с помощью отправка имени автора статьи вместе с запросом, по умолчанию фиксированное значение Baeldung, только если запрос не имеет параметра автора.

public Result writer(String author) {
    return ok("REST API with Play by " + author);
}

Итак, давайте еще изменим действие записи, добавив параметр:

GET     /writer           controllers.HomeController.writer(author = "Baeldung")
GET     /writer/:author   controllers.HomeController.writer(author: String)

Давайте также посмотрим, как добавить к маршруту параметр с фиксированным значением:

Обратите внимание, что теперь у нас есть два отдельных маршрута. что приводит к действию HomeController.index вместо одного.

Routing in Play by Baeldung

Теперь, когда мы загружаем http://localhost:9000/writer из браузера, мы получаем:

Routing in Play by john

И когда мы загружаем http://localhost:9000/writer/john, мы получаем:

8.2. Параметры со значениями по умолчанию

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

Разница между ними заключается в том, что фиксированные значения используются в качестве резерва для параметров пути, а значения по умолчанию используются в качестве резерва для параметров запроса.

Параметры пути имеют вид http://localhost:9000/param1/param2, а параметры запроса имеют вид http://localhost:9000/?param1=value1\u0026param2=value2.

author = "Baeldung"

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

author ?= "Baeldung"

В то время как значения по умолчанию используют другой тип присваивания:

Мы используем оператор ?=, который условно присваивает Baeldung автору в случае, если автор найден не содержат значения.

Для полной демонстрации давайте создадим действие HomeController.writer. Скажем, помимо имени автора, которое является параметром пути, мы также хотим передать идентификатор автора в качестве параметра запроса, который по умолчанию должен быть равен 1, если он не передается в запросе.

public Result writer(String author, int id) {
    return ok("Routing in Play by: " + author + " ID: " + id);
}

Мы изменим действие записи на:

GET     /writer           controllers.HomeController.writer(author="Baeldung", id: Int ?= 1)
GET     /writer/:author   controllers.HomeController.writer(author: String, id: Int ?= 1)

и маршрут записи к:

Routing in Play by: Baeldung ID: 1

Теперь загружая http://localhost:9000/writer мы видим:

Routing in Play by: Baeldung ID: 10

Нажатие http://localhost:9000/writer?id=10 дает нам:

Routing in Play by: john ID: 1

А как насчет http://localhost:9000/writer/john?

Routing in Play by: john ID: 5

И, наконец, http://localhost:9000/writer/john?id=5 возвращает:

9. Заключение

В этой статье мы рассмотрели понятие маршрутизации в игре. Приложения. У нас также есть статья о создании RESTful API с Play Framework, где концепции маршрутизации, описанные в этом руководстве, применяются на практическом примере.