Обработка строки запроса от пользователя #
Строка запроса #
В рамках URI, который передаётся от клиента к серверу, можно передать набор именованных параметров в строке запроса. Строка запроса отделяется от пути знаком ?, затем идут пары параметров в формате название=значение, разделённые символом &. Рассмотрим следующий пример:
http://localhost:9000/objects?shape=triangles&min-area=90
Запрос от клиента происходит по протоколу HTTP по пути /objects к серверу localhost:9000. Данному пути передаются два параметра:
- параметр
shapeсо значениемtriangles; - параметр
min-areaсо значением90.
В рамках одной строки запроса может быть передано несколько параметров с одинаковым названием. Эта возможность обычно используется для передачи информации из HTML-форм. Важно понять, что набор параметров невозможно свести к ассоциативному массиву с уникальными ключами.
Классы Uri и Parameters #
Библиотека http4k предоставляет удобный класс для работы с URI-идентификаторами, Uri. Все объекты класса Request предоставляют свойство uri, в котором находятся данные запроса от пользователя. Объекты класса Uri можно также создать самостоятельно из строки с помощью метода Uri.of(value: String): Uri:
val uri = Uri.of("https://lms.crafted.su/tech-notes/")Для работы со строкой запроса класс Uri предоставляет средства, отражённые в списке ниже.
- Свойство
queryдля низкоуровневого доступа к строке, пришедшей от пользователя. - Функция
queries(): Parametersпозволяет получить доступ к списку параметров для получения значений параметров. - Функция
query(name: String, value: String?): Uriдобавляет ещё один параметр к запросу. При применении данной функции создаётся новый объект класса Uri, в значение свойстваqueryкоторого записывается строка с добавленным параметром. Оригинальный объект не изменяется. - Функция
removeQuery(name: String): Uriудаляет все вхождения параметров с данным названием. - Функция
fun query(query: String): Uriпозволяет заменить целиком строку запроса на новую. Результатом работы функции является новый объект Uri, включающий новую строку запроса. Оригинальный объект класса Uri не изменяется.
Parameters является псевдонимом для списка параметров:
typealias Parameters = List<Parameter>Для получения значений из данного списка можно воспользоваться средствами, описанными в списке ниже.
- Функция
Parameters.findSingle(name: String): String?позволяет выполнить поиск первого значения для параметра с указанным названием. - Функция
Parameters.findMultiple(name: String): List<String?>позволяет найти все значения для параметра с указанным названием. - Функция
Parameters.toParametersMap(): Map<String, List<String?>>позволяет преобразовать параметры в ассоциативный массив, где для каждого названия (строки) будет указан список переданных значений.
Задача № 1. Протестируйте работу классов Uri и Parameters #
Реализуйте тесты для следующих свойств классов.
- Пользователь класса Uri может получить доступ к низкоуровневому содержимому строки запроса.
- Пользователь класса Uri может удалить существующий параметр из строки запроса.
- Пользователь класса Uri может добавить новый параметр в строку запроса.
- Пользователь класса Uri может добавить новый параметр с существующим именем.
- Пользователь классов Uri и Parameters может получить значения одиночных параметров.
- Пользователь классов Uri и Parameters может получить список значений множества параметров с одним именем.
- Пользователь классов Uri и Parameters получает null-значения при поиске значений несуществующих параметров.
Проблема отображения большого объёма данных #
При работе в среде распределённых систем не принято обмениваться большими объёмами данных. Большая часть взаимодействия происходит небольшими по объёму документами. Это снижает вероятность повреждения документов при передаче и ускоряет их получение финальным адресатом.
Данный вопрос особо актуален для клиентов, которые пользуются ненадёжными или перегруженными средствами связи: мобильный доступ к сети Интернет, публичные точки доступа WiFi, особенно в сельской местности.
Можно также привести следующие аргументы за частичное предоставление информации пользователю:
- Большой объём данных долго обрабатывает система (как на сервере, так и на клиенте), что делает работу пользователя с системой неудобной.
- Пользователю неудобно работать с очень большими документами. Поэтому, например, от свитков отказались и перешли на систему страниц.
- Системы хотят самостоятельно обрабатывать свои данные и не делиться этими данными с другими системами.
Типичным примером документа с большим объёмом данных является страница с показом информации со списком данных, например списком товаров в магазине или выдача в поисковом запросе. Для обеспечения быстроты и удобства работы пользователя данные в списке разделяются на части, каждая из которых отображается на своей странице.
Сценарий постраничного вывода #
Рассмотрим базовый сценарий реализации постраничного вывода.
sequenceDiagram autonumber actor user as Пользователь participant brow as Браузер participant serv as Сервер user ->> brow : Открывает ссылку со списком brow ->> serv : Выполняет GET-запрос документа по ссылке serv ->> brow : HTML-документ с данными и навигационной панелью brow ->> user : Отображает полученный документ user ->> brow : Выбирает номер страницы на панели и нажимает на ссылку brow ->> serv : Посылает GET-запрос на документ с номером страницы serv ->> brow : HTML-документ с данными и навигационной панелью brow ->> user : Отображает документ с нужной страницей
Ключевые особенности подхода:
- У маршрута для отображения документа появляется необязательный параметр — номер страницы.
- По умолчанию номер страницы равен единице (или нулю в зависимости от удобства дальнейшей обработки).
- На HTML-странице отображается навигационная панель между страницами. Навигационная панель может быть информативной, либо состоять из одной кнопки «показать больше».
- Внутри навигационной панели находятся подготовленные ссылки, включающие номера страниц, на которые пользователь может перейти.
- Пользователь может скопировать ссылку и поделиться ей с другими пользователями.
В ссылках на другие страницы необходимо сохранять все параметры, которые были переданы при отправке на страницу. Это необходимо для сохранения параметров, которые пользователь ввёл ранее или они были заданы . Эти параметры необходимо использовать при формировании ссылок постраничного вывода, чтобы не потерять данные.
Задача № 2. Формирование данных для постраничного вывода #
Создайте класс Paginator, в задачу которого должно входить формирование данных для отображения компонента переходами между страниц. Данный класс должен предоставлять следующие данные:
- Есть ли возможность перейти к предыдущей странице или нет.
- Список страниц (номер страницы и URI к странице) для передвижения назад.
- Есть ли возможность перейти к следующей странице или нет.
- Список страниц (номер страницы и URI к страние) для передвижения впрёд.
- Номер текущей страницы.
Базовыми данными для вычисления этих параметров можно использовать:
- Базовый URI-адрес страницы.
- Текущий номер страницы.
- Количество страниц.
Напишите модульные тесты для данного класса.
Задача № 3. Отображение #
Модифицируйте приложение для отображения списка треугольников. Реализуйте постраничный вывод информации о треугольниках на странице списка треугольников, которые вводились в систему. Для облегчения реализации:
- Инициализируйте список треугольников 10 разными треугольниками.
- Ограничьте количество данных на одной странице 6 треугольниками.
Рекомендуется реализовать метод внутри класса-списка треугольников, возвращающий подмножество списка треугольников в зависимости от переданной страницы. Страница передаётся в качестве аргумента метода.
fun trianglesByPageNumber(pageNumber: Int): List<Triangles>Также потребуется реализовать метод для подсчёта количества страниц в классе-списке треугольников:
fun pageAmount(): IntПроверьте краевые ситуации:
- В списке треугольников нет нужных данных.
- На последней страницы корректно отображается укороченный список.
- При передаче номера страницы за пределами возможных значений приложение показывает пустой список.
Используйте данные методы и созданный класс Paginator для реализации постраничного отображения данных в списке треугольников.