1. Обзор
Маршрутизация-это общая концепция, которая появляется в большинстве фреймворков веб-разработки, включая Spring MVC .
Маршрут-это шаблон URL-адреса, который сопоставляется обработчику. Обработчиком может быть физический файл, например загружаемый ресурс в веб-приложении, или класс, обрабатывающий запрос, например контроллер в приложении MVC.
В этом уроке мы рассмотрим аспект маршрутизации при разработке веб-приложений с помощью Play Framework .
2. Настройка
Во-первых, нам нужно будет создать приложение Java Play. Подробная информация о том, как настроить игровой фреймворк на компьютере, доступна в нашей вводной статье .
К концу настройки у нас должно быть работающее игровое приложение, к которому мы можем получить доступ из браузера.
3. HTTP-маршрутизация
Итак, как Play знает, с каким контроллером следует консультироваться всякий раз, когда мы отправляем HTTP-запрос? Ответ на этот вопрос содержится в файле конфигурации app/conf/routes .
Маршрутизатор Play преобразует HTTP-запросы в вызовы действий. HTTP-запросы считаются событиями в архитектуре MVC , и маршрутизатор реагирует на них, консультируясь с файлом routes , для какого контроллера и какое действие в этом контроллере необходимо выполнить.
Каждое из этих событий предоставляет маршрутизатору два параметра: путь запроса со строкой запроса и метод HTTP запроса.
4. Базовая Маршрутизация С Игрой
Чтобы маршрутизатор выполнял свою работу, файл conf/routes должен определять сопоставления методов HTTP и шаблонов URI с соответствующими действиями контроллера:
GET / controllers.HomeController.index GET / assets/*file controllers.Assets.versioned(path="/public", file: Asset)
Все файлы маршрутов также должны сопоставлять статические ресурсы в папке play-routing/public , доступной клиенту на конечной точке /assets .
Обратите внимание на синтаксис определения HTTP-маршрутов и действие HTTP-метода space
URI pattern space
controller.
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
Бывает так, что если наш параметр действия имеет строковый тип , мы можем передать его во время вызова действия без указания типа параметра , хотя это не то же самое для других типов.
Давайте оживим нашу /отличную конечную точку информацией о возрасте.
Вернуться к Home Controller отличное действие, мы изменим его на:
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 . Игровой фреймворк построен в Scala. Следовательно, существует много синтаксиса scala.
Давайте загрузимся http://localhost:9000/greet/john/26 :
Hello john, you are 26 years old
6.2. Подстановочные знаки в параметрах пути
В нашем файле конфигурации маршрутов последнее сопоставление:
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
Мы используем подстановочный знак в динамической части пути. Мы говорим, что любое значение, заменяющее *файл в фактическом запросе, должно быть проанализировано в целом, а не декодировано, как в других случаях параметров пути.
В этом примере контроллер является встроенным контроллером Assets , который позволяет клиенту загружать файлы из папки play-routing/public . Когда мы загружаем http://localhost:9000/assets/images/favicon.png , мы должны увидеть изображение игрового фавикона в браузере, так как он присутствует в папке /public/images .
Давайте создадим наш собственный пример действия в HomeController.java :
public Result introduceMe(String data) { String[] clientData = data.split(","); return ok("Your name is " + clientData[0] + ", you are " + clientData[1] + " years old"); }
Обратите внимание, что в этом действии мы получаем один строковый параметр и применяем нашу логику для его декодирования. В этом случае логика состоит в том, чтобы разбить разделенную запятыми строку | на массив. Раньше мы зависели от маршрутизатора, чтобы декодировать эти данные для нас.
С подстановочными знаками мы сами по себе. Мы надеемся, что клиент получит правильный синтаксис при передаче этих данных. В идеале, мы должны проверить входящую строку перед ее использованием|/.
Давайте создадим маршрут к этому действию:
GET /*data controllers.HomeController.introduceMe(data)
Теперь загрузите URL-адрес http://localhost:9000/john,26 . Это приведет к печати:
Your name is john, you are 26 years old
6.3. Регулярное выражение в параметрах пути
Так же, как и подстановочные знаки, мы можем использовать регулярные выражения для динамической части. Давайте добавим действие, которое получает число и возвращает его квадрат:
public Result squareMe(Long num) { return ok(num + " Squared is " + (num * num)); }
Теперь мы добавим его маршрут:
GET /square/$num<[0-9]+> controllers.HomeController.squareMe(num:Long)
Давайте разместим этот маршрут ниже представьте меня маршрут, чтобы представить новую концепцию. Мы можем обрабатывать маршруты только там, где часть регулярного выражения является положительным целым числом с этой конфигурацией маршрутизации.
Теперь, если мы разместили маршрут, как указано в предыдущем абзаце, и мы загружаем http://localhost:9000/square/2 , нас должно приветствовать исключение ArrayIndexOutOfBoundsException :
Если мы проверим журналы ошибок в консоли сервера, мы поймем, что вызов действия был фактически выполнен на introduceMe action, а не на squareMe action. Как уже говорилось ранее о подстановочных знаках, мы сами по себе и не проверяли входящие данные.
Вместо строки, разделенной запятыми, метод introduceMe был вызван со строкой ” square/2 “. Следовательно, после его разбиения мы получили массив первого размера. Попытка достичь индекса 1 затем выкинул исключение.
Естественно, мы ожидаем, что вызов будет перенаправлен в метод square . Почему он был направлен на представить меня ? Причина в том, что функция воспроизведения, которую мы рассмотрим далее, называется Приоритет маршрутизации.
7. Приоритет маршрутизации
Если существует конфликт между маршрутами , как между square Me и introduct Me , то Play выбирает первый маршрут в порядке объявления .
Почему возникает конфликт? Из-за подстановочного контекста путь /*data соответствует любому URL-адресу запроса, кроме базового пути / . Таким образом, каждый маршрут, шаблон URI которого использует подстановочные знаки, должен отображаться последним по порядку .
Теперь давайте изменим порядок объявления маршрутов таким образом, чтобы они вводили меня маршрут следовал за квадратным мной и перезагружали:
2 Squared is 4
Чтобы проверить силу регулярных выражений в маршруте, попробуйте загрузить http://locahost:9000/square/-1 , маршрутизатор не сможет соответствовать маршруту squareMe . Вместо этого он будет соответствовать представьте меня, и мы снова получим ArrayIndexOutOfBoundsException .
Это потому, что -1 не совпадает ни с предоставленным регулярным выражением, ни с каким-либо алфавитным символом.
8. Параметры
До этого момента мы рассматривали синтаксис объявления типов параметров в файле маршрутов.
В этом разделе мы рассмотрим дополнительные опции, доступные нам при работе с параметрами в маршрутах.
8.1. Параметры С Фиксированными Значениями
Иногда мы хотим использовать фиксированное значение для параметра. Это наш способ сказать Play использовать предоставленный параметр path или , если контекст запроса является путем / , использовать определенное фиксированное значение.
Другой способ взглянуть на это — иметь две конечные точки или контекстные пути, ведущие к одному и тому же действию контроллера, причем одна конечная точка требует параметра из URL-адреса запроса и по умолчанию переходит к другой, если указанный параметр отсутствует.
Чтобы продемонстрировать это, давайте добавим действие writer() в HomeController :
public Result writer() { return ok("Routing in Play by Baeldung"); }
Предполагая, что мы не всегда хотим, чтобы наш API возвращал Строку :
Routing in Play by Baeldung
Мы хотим контролировать его, отправляя имя автора статьи вместе с запросом, по умолчанию используя фиксированное значение Baeldung только в том случае, если в запросе нет параметра author .
Итак, давайте еще раз изменим действие writer , добавив параметр:
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 вместо одного.
Когда мы сейчас загрузимся http://localhost:9000/writer из браузера мы получаем:
Routing in Play by Baeldung
И когда мы загружаем http://localhost:9000/writer/john , мы получаем:
Routing in Play by john
8.2. Параметры Со Значениями По Умолчанию
Помимо фиксированных значений, параметры также могут иметь значения по умолчанию. Оба предоставляют резервные значения для параметров действия контроллера в случае, если запрос не предоставляет требуемых значений.
Разница между ними заключается в том, что фиксированные значения используются в качестве запасного варианта для параметров пути, в то время как значения по умолчанию используются в качестве запасного варианта для параметров запроса .
Параметры пути имеют вид http://localhost:9000/param1/param2 и параметры запроса имеют вид http://localhost:9000/?param1=value1¶m2=value2 .
Второе различие заключается в синтаксисе объявления этих двух в маршруте. Параметры с фиксированным значением используют оператор присваивания, как в:
author = "Baeldung"
В то время как значения по умолчанию используют другой тип назначения:
author ?= "Baeldung"
Мы используем оператор ?= , который условно присваивает Baeldung автору в случае, если автор не содержит значения.
Чтобы иметь полную демонстрацию, давайте создадим действие HomeController.writer . Допустим, помимо имени автора, которое является параметром пути, мы также хотим передать author id в качестве параметра запроса, который по умолчанию должен иметь значение 1 если не передано в запросе.
Мы изменим writer action на:
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)
Теперь загрузка http://localhost:9000/writer мы видим:
Routing in Play by: Baeldung ID: 1
Наезд http://localhost:9000/writer?id=10 дает нам:
Routing in Play by: Baeldung ID: 10
Как насчет http://localhost:9000/writer/john ?
Routing in Play by: john ID: 1
И, наконец, http://localhost:9000/writer/john?id=5 возврат:
Routing in Play by: john ID: 5
9. Заключение
В этой статье мы исследовали понятие маршрутизации в игровых приложениях. У нас также есть статья о создании RESTful API с Play Framework, в которой концепции маршрутизации в этом руководстве применяются на практическом примере.
Как обычно, исходный код этого учебника доступен на GitHub .