Обработка переменных пути #
Документация #
Описание маршрутизатора и обработчика с переменной пути #
При описании маршрутов в маршрутизаторе http4k разработчик может указать переменную пути:
val triangleStorage = TriangleStorage()
val triangleHandler = TriangleHandler(triangleStorage)
routes(
"/v1/triangles/{triangle-id}" bind Method.GET to triangleHandler,
)Внутри обработчика можно получить доступ к переменной пути с помощью метода Request.path:
class TriangleHandler(
private val triangleStorage: TriangleStorage,
private val jsonFactory: JsonFactory
) : HttpHandler {
override fun invoke(request: Request): Response {
val rawId: String? = request.path("triangle-id")
...
}
}- Данные приходят в строковом формате.
- Если пользователь не передал данные, то и обработчик не будет вызван.
Как работает функция Request.path
#
Рассмотрим реализацию функции Request.path:
fun Request.path(name: String): String? = when (this) {
is RoutedMessage ->
(xUriTemplate ?: throw IllegalStateException("Request was not routed, so no uri-template present"))
.extract(uri.path)[name]
else -> throw IllegalStateException("Request was not routed, so no uri-template present")
}Из реализации видно, что функция работает не с интерфейсом Request, а с
интерфейсом RoutedMessage. Единственный класс, который реализует данный
интерфейс внутри библиотеки http4k — это класс
RequestWithContext,
обладающий следующим конструктором:
constructor(delegate: Request, uriTemplate: UriTemplate)Таким образом, объект класса RequestWithContext соединяет в себе оригинальный
объект запроса и шаблон подсистемы маршрутизации. Подсистема маршрутизации http4k
создаёт этот объект автоматически, поэтому внутри HTTP-обработчика переменную
пути можно получить напрямую через Request.path.
Описание шаблона пути происходит с помощью класса
UriTemplate,
который создаётся функцией-конструктором
UriTemplate.from(template: String). Например:
val template = UriTemplate.from("/v1/triangle/{triangle-id}")Задание № 1. Расширение возможностей хранилища #
Для расширенного доступа к данным приложения добавьте следующие возможности в класс-хранилище треугольника:
- Получение треугольника по его уникальному идентификатору. Реализуйте функцию
fun getById(id: UUID): Triangle?. Если треугольник с таким идентификатором не найден, функция возвращаетnull. - Добавление треугольника. Реализуйте функцию
fun add(triangle: Triangle): UUID. Функция возвращает назначенный идентификатор. Для получения уникального идентификатора используйте функциюUUID.randomUUID().
Задание № 2. Реализация отображения конкретного треугольника #
Реализуйте отображение информации о конкретном треугольнике по пути
/v1/triangles/{triangle-id}. Если по указанному пути треугольник не был найден, то
обработчик должен возвращать сообщение со статусом 404, NOT_FOUND.
Ключевые части по решению задачи:
- Реализуйте HTTP-обработчик для отображения информации о треугольнике:
- Он должен принимать в качестве аргумента объекты генератора JSON и хранилища.
- Из запроса извлеките значение переменной пути
triangle-id, преобразуйте его кUUIDфункциейUUID.fromString(name: String): UUID. При некорректном значении эта функция возбуждает исключениеIllegalArgumentException, которое необходимо обработать. - Если значение переменной пути не является корректным UUID, то должен быть
возвращён ответ с кодом 400, BAD REQUEST, и следующим JSON-документом.
Случай, когда переменная пути не передана вовсе, рассматривается в задании
№ 3.
Вместо
{ "error": "Некорректный идентификатор треугольника. Для параметра triangle-id ожидается UUID, но получено значение «ЗНАЧЕНИЕ»" }ЗНАЧЕНИЕподставьте фактическое значение переменной пути, полученное из запроса. Полеerrorформируется динамически. ЗаголовокContent-Typeответа должен иметь значениеapplication/json. - Вызовите функцию хранилища, передав ей полученный идентификатор треугольника.
- Если извлечь данные не удалось (функция вернула
null), то обработчик должен вернуть ответ с кодом 404, NOT FOUND, и следующим JSON-документом:Значение поля{ "Id": "f0dcea8c-7a56-4223-ae43-3c664b50a603", "error": "Треугольник не найден" }Id— идентификатор из переменной пути. ЗаголовокContent-Typeответа должен иметь значениеapplication/json. - При успешном получении треугольника верните ответ со статусом 200, OK.
JSON-документ формируется из полей объекта
Triangle(модель и правила преобразования описаны в занятии «Формат JSON»). Пример документа:{ "Id": "66ade672-2301-4408-9471-8a17aa883d65", "SideA": 1, "SideB": 2, "SideC": 3, "RegistrationDateTime": "2024-07-01T00:00:00.0", "BorderColor": "BLUE", "FillColor": "GREEN", "Description": "странные данные, это не треугольник", "Area": null, "Type": "некорректный" }
- Реализуйте модульные тесты на созданный HTTP-обработчик. В тестах создавайте
объект
Requestпрограммно и проверяйте кодStatusи заголовокContent-Typeполученного объектаResponse. Пример построения запроса:
val request = Request(Method.GET, "/v1/triangles/f0dcea8c-7a56-4223-ae43-3c664b50a603")
val response = handler(request)- Свяжите данный обработчик HTTP-запроса с маршрутом
/v1/triangles/{triangle-id}.
Задание № 3. Обработка отсутствия параметра #
Шаблон /v1/triangles/{triangle-id} требует непустого значения переменной
пути. Запрос по пути /v1/triangles/ не соответствует этому шаблону, поэтому
обработчик не вызывается, и http4k возвращает ответ с кодом 404.
Зарегистрируйте отдельный маршрут /v1/triangles/ и свяжите его с
обработчиком, который возвращает ответ с кодом 400, BAD REQUEST, и JSON-документ
с сообщением о некорректном идентификаторе треугольника (см. задание № 2).