Маршрутизация в играх приложений на Java

Маршрутизация в играх приложений в Java

1. обзор

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

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

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

2. Настроить

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

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

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

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

Маршрутизатор Play преобразует HTTP-запросы в призывы к действию. HTTP requests are considered to be events in MVC architecture, и маршрутизатор реагирует на них, консультируясь с файлом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 и действие контроллера шаблона URIspace метода HTTPspace.

5. Шаблоны URI

В этом разделе мы немного разъясним шаблоны URI.

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

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

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

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

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

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

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

6. Расширенная маршрутизация с игрой

В этом разделе мы подробно обсудим расширенные параметры маршрутизации с использованием динамических шаблонов 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)

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

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

Hello john

Так получилось, чтоif our action parameter is of string type, we may pass it during the action call without specifying the parameter type, хотя для других типов это не то же самое.

Давайте добавим в нашу конечную точку/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

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, мы должны увидеть изображение значка "Воспроизвести" в браузере, поскольку оно находится в папке/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");
}

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

С подстановочными знаками мы сами по себе. Мы надеемся, что клиент получит правильный синтаксис при передаче этих данных. В идеалеwe should validate the incoming String before using it.

Давайте проложим маршрут к этому действию:

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

Теперь загрузите URLhttp://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)

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

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

image

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

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

Естественно, мы ожидаем, что вызов будет перенаправлен на методsquareMe. Почему он был перенаправлен наintroduceMe? Причина в том, что мы рассмотрим функцию Google Play под названиемRouting Priority..

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

Если существует конфликт между маршрутами, например, междуsquareMe иintroduceMe, тоPlay picks the first route in declaration order.

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

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

2 Squared is 4

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

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

8. параметры

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

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

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

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

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

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

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

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

Routing in Play by example

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

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

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

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

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

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

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

Routing in Play by example

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

Routing in Play by john

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

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

Разница между ними в том, чтоfixed values are used as a fallback for path parameters while default values are used as a fallback for query parameters.

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

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

author = "example"

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

author ?= "example"

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

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

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

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

аwriter направляет на:

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

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

Routing in Play by: example ID: 1

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

Routing in Play by: example 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. Заключение

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

Как обычно, доступен исходный код этого руководстваover on GitHub.