Васильев Андрей Михайлович, 2024
Версии презентации
В рамках библиотеки http4k линзы применяются для решения различных задач по взаимодействию со структурами данных:
Линзы библиотеки http4k определены в пакете org.http4k.lens
В рамках пакета определены два основных интерфейса:
Для удобства построения линз применяются отдельные классы-строители
Для создания линзы необходимо указать следующие параметры:
Терминатор создаёт объект линзы из соответствующего объекта-строителя
Классы-линзы описаны в пакете org.http4k.lens
Поддерживается два типа линз:
Для стандартных классов http4k в основном предлагаются двунаправленные линзы
Создадим линзу для получения непустой строковой переменной из пути:
val parameterLens = Path.nonBlankString().of("parameter")
Для получения параметра необходимо применить лизу к источнику данных, к запросу:
val parameter: String = parameterLens(request)
nonBlankString
, т.е. обязательно вернёт StringОбъект | Внутренний тип данных | Применимо для объектов | Количество | Необходимый |
---|---|---|---|---|
Параметры запроса | String | Request | Один или много | Обязательный или необязательный |
Заголовок | String | Request / Response | Один или много | Обязательный или необязательный |
Переменная пути | String | Request | Один | Обязательный |
Поле формы | String | WebForm | Один или много | Обязательный или необязательный |
Тело | String | Request / Response | Один | Обязательный |
Для каждого целевого элемента предоставляется объект-строитель, настроенный на взаимодействие с данным целевым элементом
Данный этап можно пропустить, если необходима null-строка, однако лучше всегда явно указывать тип для преобразования
http4k предлагает поддерживает преобразование данных. Данные приходят от пользователя в строковом формате и их далее необходимо преобразовать в корректный внутренний формат
Рассмотрим список преобразований типов для объекта-запроса Query
enum()
— преобразование в перечисление по имени элементаdateTime()
— преобразование переданных данных в тип LocalDateint()
— преобразование в целочисленный вариантnonEmptyString()
— проверка строки на существованиеnonBlankString()
— проверка строки на наличие печатных символовuuid()
— преобразование в тип данных UUIDДанные могут быть преобразованы в любой формат с помощью метода map()
Терминатор определяет уровнень необходимости параметра. Терминаторы описаны в интерфейсах LensSpec и BiDiLensSpec
Ключевое отличие — поведение при отсутствии целевого значения
Терминатор создаёт на основе спецификации (строителя, LensSpec) нужную линзу
Для Path-спецификаций доступен только терминатор of()
, т.к. переменная
обязательно будет присутствовать
Рассмотрим пример линзы, которая извлекает целое число из параметров запроса
val queryDataLens = Query.int().defaulted("data", 15)
Query
int()
15
При наличии данных будет выполнено их преобразование и получение
val request = Request(GET, Uri.of("http://example.ru"))
val requestWithQuery = request.query("data", "42")
val data = queryDataLens(requestWithQuery) // 42
При отсутствии данных линза вернёт значение по умолчанию
val request = Request(GET, Uri.of("http://example.ru"))
val data = queryDataLens(request) // 15
При отсутствии данных будет выброшено исключение
val request = Request(GET, Uri.of("http://example.ru"))
val requestWithQuery = request.query("data", "abc")
queryDataLens(requestWithQuery)
org.http4k.lens.LensFailure: query 'data' must be integer
Выброс исключения при использовании линз должен быть обработан:
Если пользователь самостоятельно может ввести некорректные данные, то необходимо обеспечить обработку данных на стороне сервера
Терминатор | Данные есть | Данных нет | Некорректные данные |
---|---|---|---|
defaulted | Данные | Значение по умолчанию | Исключение |
optional | Данные | null | Исключение |
required | Данные | Исключение | Исключение |
Каждый тип терминатора продуцирует линзу, способную выбросить исключение в одной или нескольких ситуациях
Для обработки ситуации выхода за указанные границы достаточно воспользоваться обработкой на уровне всего приложения
HTML-форма представляет собой набор полей, которые пользователь заполняет
В рамках http4k используются следующие типы данных:
Порядок использования элементов следующий:
Форма для отправки обратной связи
<form method="POST">
<div class="mb-3">
<label for="age" class="form-label">Возраст</label>
<input type="number" class="form-control" id="age" name="age" required>
</div>
<div class="mb-3">
<label for="name" class="form-label">Имя</label>
<input type="password" class="form-control" id="password"
name="password" required>
</div>
<div class="mb-3 form-check">
<label for="feedback" class="form-label">Комментарии</label>
<textarea id="feedback" name="feedback" rows="10"></textarea>
</div>
<button type="submit" class="btn btn-primary">Отправить</button>
</form>
val ageField = FormField.int().required("age")
val nameField = FormField.nonEmptyString().required("name")
val feedbackField = FormField.nonEmptyString().optional("feedback")
val formLens = Body.webForm(Validator.Feedback,
ageField, nameField, feedbackField).toLens()
Обработка данных с формы
val form = formLens(request)
if (form.errors.isEmpty()) {
val age = ageField(form)
val feedback = feedbackField(form)
}
Можно использовать в тестах:
// Отправка пустой формы
val invalidRequest = Request(GET, "/")
.with(Header.CONTENT_TYPE of ContentType.APPLICATION_FORM_URLENCODED)
val invalidForm = formLens(invalidRequest)
println(invalidForm.errors)
// Отправка заполненной формы
val webForm = WebForm().with(ageField of 55, nameField of "Rita"))
val validRequest = Request(GET, "/").with(formLens of webForm)
val validForm = formLens(validRequest)
val age = ageField(validForm)