HTTP-приложения. Введение в http4k

HTTP-приложения #

Васильев Андрей Михайлович, 2024

Версии презентации


Интернет гипертекста #

В рамках концепции гипертекста доступ ко всем данным может быть осуществлён с помощью одного приложения, браузера, способного отображать документы и удобным образом переходить к другим документам в сети

  • Веб-браузер является средством просмотра HTML-документов
  • HTML-документы содержат текст и ссылки на другие документы
  • Документы передаются с сервера на клиент с помощью протокола HTTP
  • Для доступа к данным пользователю достаточно знать доменное имя сервера

Базовые технологии интернета гипертекста

  • Протокол обмена документами HTTP был утверждён в 1992 году
  • Язык разметки документов HTML был утверждён в 1993 году

Протокол HTTP #

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

История создания #

  • Версия 0.9 представлена в 1990-1992 годах
  • Версия 1.0 стандартизирована в 1996 году
  • Версия 1.1 стандартизирована в 1999 году
  • Версия 2 стандартизирована в 2015 году
  • Версия 3 стандартизирована в 2022 году

Новые версии обратно совместимы со старыми, добавляют новые возможности


Связанные сетевые технологии #

  • Стек сетевых протоколов TCP / IP (1980-е)
    • Транспортный протокол TCP
      • Используется в протоколах HTTP 1.1 (1999), HTTP 2 (2015)
    • Транспортный протокол UDP
      • Используется в протоколе HTTP 3 (2022)
  • Безопасность транспортного уровня TLS (1999, 1.3 — 2018)
    • Является основой для защищённого варианта HTTP, HTTPS
  • Система доменных имён DNS (1983, 1987)
  • Доменные имена поверх HTTPS, DoH (2018)

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


Архитектура HTTP #

В рамках протокола выделяются 2 роли: сервер и клиент

  • Приложение-сервер работает постоянно и ожидает подключения от клиента
  • Клиент подключается к серверу и передаёт запрос на получение документа
  • Сервер возвращает запрошенный документ, если он существует
  • Сервер закрывает соединение после передачи документа

diagram


Особенности HTTP-приложений #

  • HTTP-сервер работает постоянно, так как он не знает когда к нему планирует обратиться клиент
  • HTTP-сервер может одновременно обрабатывать запросы от множества клиентов
  • Клиент может послать очередной запрос с любой задержкой: 10мс, 2000мс и т.д.
  • В последних версиях протокола HTTP сервер может инициировать отправку данных на сторону клиента

HTTP-приложения являются распределёнными приложениями, в которых как сервер, так и клиент должен поддерживать совместное актуальное состояние


Роли участников в HTTP-взаимодействии #

Между клиентом и сервером может находится несколько проксирующих серверов

diagram

  • Задача прокси-серверов, находящихся рядом с клиентом состоит в уменьшении объёма запрашиваемых данных (в кешировании):
    • запросы могут быть одинаковыми у разных клиентов
    • один клиент может инициировать запросы к одному ресурсу
  • В современны браузерах встроен агрессивный кеширующий сервер
  • Задачи прокси-сервера, находящегося рядом с серверами, состоит в балансировке нагрузки между серверами
    • По географическому положению клиента
    • По степени нагруженности серверов
    • В зависимости от клиента

HTTP-запросы #

Изначально HTTP-протокол разрабатывался как текстовый, то есть клиент и сервер обмениваются специально оформленными текстовыми сообщениями друг с другом

В новых версиях текст был заменён на бинарное представление для ускорения передачи данных, но концептуально логика взаимодействия не изменилась

  1. Клиент открывает TCP-соединение с сервером, которое используется для отправки сообщения, и приёма ответа
  2. Клиент посылает HTTP-сообщение, например:
    GET /index.html HTTP/1.1
    Host: developer.mozilla.org
    Accept-Language: ru
  3. Сервер отвечает документом на сообщение:
    HTTP/1.1 200 OK
    Date: Sat, 09 Oct 2010 14:28:02 GMT
    ...
    <!DOCTYPE html...
  4. Клиент закрывает или переиспользует TCP-соединение для другого запроса

Разбор структуры HTTP-запроса #

GET /index.html HTTP/1.1
Host: uniyar.ac.ru
Accept-Language: ru

diagram

  • Первая строка содержит описание запроса
    • GET — метод (тип запроса)
    • /index.html — путь к документу, к которому осуществляется запрос
    • HTTP/1.1 — версия протокола
  • На следующих строках указываются заголовки запроса в формате
    название : значение
    
  • После заголовков может идти тело запроса, данные для передачи

Разбор структуры HTTP-ответа #

HTTP/1.1 200 OK
Date: Sat, 09 Oct 2010 14:28:02 GMT
...
Content-Length: 2761
Content-type: text-html; charset=utf-8

<!DOCTYPE html...

diagram

  • Первая строка описывает результаты запроса
    • HTTP/1.1 — версия протокола
    • 200 — код ответа сервера, указывающий результат обработки
    • OK — краткое текстовое описание кода ответа
  • Список заголовков ответа
  • Если в рамках ответа возвращается документ, то среди заголовков точно будет указан Content-Length, содержащий размер документа в символах
  • Через пустую строку после заголовков идёт тело документа

Группы кодов статуса #

В протоколе определены следующие группы кодов ответа

  • 100-199 — Информационные ответы
  • 200-299 — Успешные ответы
  • 300-399 — Перенаправления
  • 400-499 — Ошибки в запросе от клиента
  • 500-599 — Ошибки в работе сервера

Часто используемые ответы #

  • 200 — запрос был выполнен успешно
  • 301 — перенесён на постоянной основе
  • 302 — документ временно перенесён
  • 404 — документ не найден

Базовый шаблон взаимодействия #

В качестве клиента в HTTP-взаимодействии может выступать любое приложение, которое реализует HTTP-протокол

Зачастую таким клиентом выступает веб-браузер, который получает и отображает веб-страницу для пользователя

  • Пользователь указывает в браузере адрес документа, который хочет просмотреть
  • Браузер выполняет запрос к серверу и получает в ответ HTML-документ
  • Документ внутри себя указывает другие документы, которые нужны для отображения и работы HTML-документа:
    • Изображения и видео-файлы для отображения на странице
    • CSS-документы определяющие внешний вид элементов на странице
    • JavaScript-документы для выполнения интерактивных действий
    • WebAssembly-модули для выполнения вычислений
  • Браузер запрашивает все эти элементы и формирует из них интерактивный документ, с которым может взаимодействовать пользователь

Особенности протокола HTTP #

Протокол HTTP является простым #

  • Протокол изначально текстовый и состоит из небольшого набора элементов
  • С помощью доступных инструментов анализирутся бинарные запросы HTTP 2 и 3

Протокол HTTP является расширяемым #

Благодаря механизму заголовков можно добавлять новую семантику к запросам и ответам. Достаточно ввести соглашение между клиентом и сервером

В версии HTTP/1.1 ввели заголовок keep-alive, который говорит серверу, что клиент может в рамках одного TCP-соединения можно выполнить несколько HTTP-запросов

Формируется новый протокол поверх HTTP

diagram

Есть более 100 стандартных заголовков запросов и ответов


В HTTP каждый запрос индивидуален #

С точки зрения протокола HTTP каждый запрос отдельный запрос не зависит от других

  • На уровне протокола нельзя сослаться на предыдущий или следующий запрос
  • Внутри запроса можно передавать данные, которые связывают запросы друг с другом, но логика по их обработки выходит за пределы HTTP-протокола

Клиент должен передать всю необходимую информацию при каждом запросе

HTTP поддерживает сессии #

Благодаря заголовку Cookies сервер и браузер могут реализовать механизм сессий:

  • Клиент и сервер могут устанавливать новые значения
  • Клиент и сервер обязуются их передавать вместе с запросами и ответами
  • Данные внутри должны быть небольшого объёма

Раздача статического контента #

Задачей первых HTTP-серверов стояла раздача файлов с файловой системы

Таким образом для идентификации документов в качестве основы были взяты пути к файлам на жёстком диске в рамках ОС UNIX

  • Все данные хранятся в едином дереве каталогов
  • Разделителем пути является /

Пример пути к файлу: /var/www/site/index.html

Внешнему наблюдателю не надо показывать все файлы, а только файлы в определённом каталоге, например в /var/www/site


Идентификация документов #

Для указания местоположения целевого документа в сети Интернет используется Uniform Resource Identifier, URI

  • Представляет собой символьную строку, которая идентифицирует ресурс
  • Может быть применена как в сети Интернет, так и в других средах

Что такое URL-адрес

Структура URI #

[ схема ":" ] [ // источник ] путь [ "?" запрос ] [ "#" фрагмент ]

Блоки внутри [ ] являются опциональными и могут быть опущены

  • Схема обычно описывает протокол доступа: http, ftp, file и т.д.
  • Источник содержит адрес сервера, на котором расположен документ
  • Путь описывает местоположение документа на источнике
  • Запрос описывает дополнительные параметры к получаемому документу
  • Фрагмент содержит указание на часть документа

Идентификация в рамках протокола HTTP #

Рассмотрим веб-сервер web-app.net, раздающий документы из файловой системы

Структура ФС:

/var/www/site
├── 404.html
├── assets
│   └── main.css.map
├── feed.xml
├── index.html
├── labs
│   ├── 001-lab-01.html
│   └── 002-lab-02.html
├── labs.html
├── reference
│   ├── books.html
│   ├── editors.html
│   ├── schedule.html
│   └── tasks.html
└── topics.html
  • Схема доступа к данным — http
  • Иерархическая часть строится из имени сервера и расположения документа на сервере
  • Корневым каталогом для сервера является /var/www/site

Для файла /var/www/site/index.html URI будет

http://web-app.net/index.html

Для файла /var/www/site/reference/tasks.html

http://web-app.net/reference/tasks.html

В рамках протокола HTTP иерархия соответствует файловой системе с разделителем /


Идентификация файлов на файловой системе #

Для указания пути к локальным файлам существует собственная схема — file

file://host/path
  • Префикс схемы file://
  • Затем идёт указание имени компьютера, на котором расположен файл
  • Если имя компьютера не указано, то считается текущий компьютер localhost
  • Иерархическая часть описывает местоположение файла в рамках файловой системы
  • В качестве разделителя используется прямой слеш /
  • Для файла в ОС семейства Windows
    • Путь: C:\my-site\information.html
    • URI с хостом: file://localhost/C:/my-site/information.html
    • URI: file:///C:/my-site/information.html
  • Для файла в Unix-подобных ОС
    • Путь: /var/www/my-site/index.html
    • URI с хостом: file://localhost/var/ww/my-site/index.html
    • URI: file:///var/www/my-site/index.html

Полные и частичные URI #

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

  • Ссылка может вести на документ на другом сервере (обязательно полный URI)
  • Ссылка может вести на документ на текущем сервере

В рамках последней схемы удобно использовать частичные URI

Частичный URI #

  • При формировании частичного URI не надо указывать ни схему, ни название сервера, на котором находится документ, только путь
  • Частичные URI могут быть использованы только внутри документа для описания пути к другим документам, пользователям браузер показывает обычно полные URI
  • Клиент (браузер) при обращении по частичной ссылке преобразует её в абсолютную
URI документа = абсолютный URI текущего документа + частичный URI

Частичные URI могут быть относительными или абсолютными


Относительные и абсолютные частичные URI #

  • Абсолютный частичный путь — из корня сайта
  • Относительный частичный путь — путь относительно текущего документа

Отличить абсолютный путь от относительного легко — он начинается с символа /

/
├── css
│   └── style.css
├── index.html
└── topic
    ├── list.html
    ├── topic-1.html
    └── topic-2.html
  • Абсолютный частичный путь к документу index.html: /index.html
  • Абсолютный частичный путь к документу style.css: /css/style.css

Они будут одинаковыми для любого просматриваемого документа на сайте

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

  • Для файла index.html таким каталогом каталогом является /
  • Для файла topic-2.html/topic

Абсолютные частичные URI на документы #

При формировании ссылки к документу внутри одного сайта удобно использовать абсолютный путь

Рассмотрим структуру сайта example-app.net

/
├── css
│   └── style.css
├── index.html
└── topic
    ├── list.html
    ├── topic-1.html
    └── topic-2.html

В рамках сайта у каждого файла будет свой собственный абсолютные частичные URL:

  • style.css: /style.css
  • index.html: /index.html
  • list.html: /topic/list.html
  • topic-1.html: /topic/topic-1.html
  • topic-2.html: /topic/topic-2.html

Пример построения относительных путей #

Построим относительный путь от файла /index.html

/
├── css
│   └── style.css
├── index.html
└── topic
    ├── list.html
    ├── topic-1.html
    └── topic-2.html
  1. Пользователь обращается к стартовой странице http://example-app.net/index.html
  2. Для оформления содержимого в документе указана ссылка на CSS-стили: css/style.css
  3. Клиентское приложение формирует абсолютный путь
    1. Высчитывается расположение документа, /
    2. К пути добавляется относительный путь css/style.css: /css/style.css
    3. Формируется URL, в котором добавляется схема, и имя сервера http://example-app.net: http://example-app.net/css/style.css

Относительный путь к родительскому каталогу #

Рассмотрим построение пути от файла topic-1.html к файлу /css/style.css

/
├── css
│   └── style.css
├── index.html
└── topic
    ├── list.html
    ├── topic-1.html
    └── topic-2.html

Для обращения к родительскому каталогу следует использовать специальное название каталога — ..

Этот специальный каталог присутствует и в обычной файловой системе и может быть использован не только в

  1. Пользователь обращается к стартовой странице http://example-app.net/topic/topic-1.html
  2. Для оформления содержимого в документе указана ссылка на CSS-стили: ../css/style.css
  3. Клиентское приложение формирует абсолютный путь
    1. Высчитывается расположение документа на сервере, /topic/
    2. К пути добавляется относительный путь ../css/style.css:
      1. /topic/../css/style.css
      2. /css/style.css
    3. Формируется URL, в котором добавляется схема, и имя сервера http://example-app.net: http://example-app.net/css/style.css

Какие URI использовать? #

Выделили следующие типы URI:

  • Полный URI, включающий схему и имя хоста и путь к файлу
  • Абсолютный частичный URI, включающий путь к файлу от корня
  • Относительный частичный URI, включающий путь от расположения файла

При ссылке на документ на другом сайте #

Возможно использовать только полный URI

Ссылка на документ собственного сайта #

Удобно использовать частичный URI, так как:

  • Он значительно короче полного пути, его легче использовать
  • Адрес сайта может измениться за время его существования

Абсолютный или относительный частичный URI? #

К плюсам использования относительного частичного URI относят:

  • Возможность работы с документами как через веб-сервер (схема http), так и через уровень файловой системы (схема file)
  • Возможность размещения документов сайта не только в корне сервера
  • Возможность предоставления одинаковых документов в разных частях нескольких сайтов (может быть и недостатком)
  • Возможность предоставления документов другим пользователям

Ограничения относительных частичных URI:

  • Некоторые документы требуют указания абсолютных частичных URI
  • Для формирования относительных URI может потребоваться больше усилий

Обзор библиотеки http4k #

http4k — это набор инструментов для создания серверных и клиентских HTTP-приложений

  • Предоставляет функциональный интерфейс для решения задач
  • Не реализует самостоятельно функции сервера и клиента, а использует промышленные технологии для реализации нужных функций
  • Включает средства для решения следующих задач:
    • Обработка HTTP-запросов
    • Взаимодействие с помощью WebSocket
    • Запуск с собственным сервером, без сервера, встраивается в Jakarta
    • Реализация контрактов OpenApi 3 (Swagger)
    • Работа со множеством шаблонизаторов
    • Поддержка работы со множеством типов данных: JSON, XML, YAML
    • Поддержка всех видов тестирования веб-приложений

Ключевые концепции http4k #

  • Основано на базовых концепциях функциональных языков программирования
  • Вся логика по обработке HTTP-запросов описывается одной функцией
  • Не используются техники метапрограммирования для решения задач
  • Минимальный объём зависимостей
  • Поддержка разработки приложения через написание тестов без сложной инфраструктуры
  • Быстрый запуск и остановка приложений
  • Предоставляет средства строгой типизации для обработки HTTP-сообщений
  • Поддержка контрактов: генерация документации OpenApi, логичное поведение «по умолчанию»

Ключевые типы http4k #

Библиотека http4k предоставляет набор функциональных типов, которые позволяют создавать, тестировать и разворачивать HTTP-приложения

HttpMessage #

Данная структура является неизменяемой и описывает запросы и ответы

  • Неизменяемые структуры позволяют избегать множества ошибок
  • Цепочка формирования структур позволяет отследить изменения в приложении
  • Позволяет удобно тестировать логику работы приложения

Детали интерфейса HttpMessage #

diagram

Класс содержит следующие свойства

  • version — версия протокола HTTP
  • headers — список HTTP-заголовков
  • body — тело сообщения

Класс предоставляет методы для установки

  • Значения конкретного свойства
  • Значений отдельных заголовков

Запросы и ответы #

Для представления этих элементов внутри приложения используются интерфейсы Request и Response, которые унаследованы от интерфейса HttpMessage

interface Request : HttpMessage #

Помимо полей интерфейса HttpMessage предоставляет следующие поля

  • method — HTTP-метод запроса (GET, PUT, …)
  • source — сетевая информация об источнике запроса
  • uri — адрес документа в запросе

interface Response : HttpMessage #

Помимо полей интерфейса HttpMessage предоставляет следующие поля

  • status — статус HTTP-ответа

Тип HttpHandler #

typealias HttpHandler = (Request) -> Response

Данный функциональный тип моделирует работу входящих и исходящих HTTP-запросов

  • Сервер должен предоставить реализацию данного функционального интерфейса, т.е. реализовать логику по обработке запроса и формирования ответа
  • Клиент должен выполнить сетевой запрос к удалённому серверу и получить ответ на поставленный запрос

Использование клиента #

Библиотека http4k предоставляет обвязку поверх множества существующих клиентов:

val client: HttpHandler = ApacheClient()

Запуск сервера #

Функция может быть связана с сервером с помощью 1 строки кода. Это позволяет отделить бизнес-логику от реализации сервера:

val app: HttpHandler = // ...
val jettyServer = app.asServer(Netty(9000)).start()

Библиотека http4k позволяет использовать множество существующих серверов:

  • Apache
  • Jetty
  • Ktor
  • Netty
  • Ratpack
  • Undertow

Создание проекта с помощью генератора #

Проект создать можно с помощью:

  • С помощью генератора проектов https://toolbox.http4k.org/project
  • Инструмента командного интерфейса http4k
  • Создать проект вручную или с помощью генератора проектов от Gradle

Шаги по созданию проекта с помощью генератора #

  1. Project core
    1. Kind of app writing: Server
    2. Server-side WebSocket: No
    3. Server engine: Netty
    4. HTTP client library: OkHttp
  2. Functional modules
    1. JSON serialization library: Jackson
    2. Templating library: Pebble
    3. Functional modules: Multipart form
    4. Functional modules, inetgrations: None
  3. Testing: Kotest
  4. Application identity
    1. Main class name: WebApplication
    2. Base package name: ru.uniyar
  5. Build tooling:
    1. Build tool: Gradle
    2. Packaging type: zip

Важные особенности проекта Gradle #

Файл gradle.properties содержит версии библиотек и используемых инструментов

junitVersion=5.10.0
http4kVersion=5.8.1.0
kotlinVersion=1.9.10

Файл build.gradle описывает параметры сборки и запуска приложения

apply plugin: "application"
mainClassName = "su.yarsu.WebApplicationKt"

dependencies {
    implementation("org.http4k:http4k-client-okhttp:${http4kVersion}")
    implementation("org.http4k:http4k-core:${http4kVersion}")
    implementation("org.http4k:http4k-multipart:${http4kVersion}")
    implementation("org.http4k:http4k-format-jackson:${http4kVersion}")
    implementation("org.http4k:http4k-server-netty:${http4kVersion}")
    implementation("org.http4k:http4k-template-pebble:${http4kVersion}")
}

Запуск приложения из шаблона #

При прохождении мастера по созданию приложения была использована система сборки Gradle, которая будет использована при проверке лабораторных работ

С её помощью приложение можно легко запустить, выполнив команду в командном интерфейсе:

./gradlew run

После выполнения всех необходимых действий по скачиванию зависимостей и сборке приложения, оно будет запущено и веб-сервер будет доступен по http://localhost:9000


Создание HTTP-обработчика #

Обработчиком HTTP-запроса является является функциональный тип HttpHandler

Он может быть создан либо с использованием лямбда-выражения:

val handler: HttpHandler = { request: Request -> Response(OK) }

Либо с использованием класса, реализующего данный функциональный тип:

class SomeHandler : HttpHandler {
    override fun invoke(request: Request) : Response = Response(OK)
}
val handler = SomeHandler()
  • В любом случае входными данными является класс Request
  • Выходными данными функции является объект класса Response

Формирование ответа #

Для описания HTTP-ответа используется класс Response

  • Минимально необходимая информация — HTTP-статус ответа, который описывает класс Status, предоставляющий множество констант
  • Для установки других частей ответа предоставляются соответствующие функции:
    • body — установить новое тело, строку для ответа
    • header — установить новое значение для конкретного заголовка
    • headers — установить новое значение для набора заголовков

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


Статусы ответов #

OK, успешный результат #

  • HTTP код: 200
  • Назначение: устанавливается когда по запросу был найден документ

NOT_FOUND, документ не найден #

  • HTTP код: 404
  • Назначение: устанавливается, когда по запросу не был найден документ
  • Ответ приложения на библиотеке http4k по умолчанию

FOUND, перенаправление #

  • HTTP код: 302
  • Назначение: перенаправить к адресу документа после создания новой сущности

Тело ответа #

Тело ответа передаётся клиенту для дальнейшей обработки. Его можно:

  • Сформировать строку вручную
  • Использовать специальные подсистемы для формирования ответа
    • Предоставление структурированных документов JSON, XML, GraphQL и т.д.
    • Предоставление текстовых HTML-документов
    • Предоставление произвольных данных в виде текста

Для установления тела ответа объекты Response предоставляют метод body:

val response = Response(OK).body("Важное сообщение")

Данные для формирования ответа #

Зачастую информации из запроса недостаточно для формирования ответа:

  • Необходимо получить доступ к данным из базы данных
  • Необходимо выполнить проверку уровня доступа пользователя
  • Необходимо обратиться к ресурсам

Для решения этих задач в конструктор обработчика надо передать зависимости:

// Конструктор в виде класса
class UserListerHandler(private val users: List<String>): HttpHandler {
    override fun invoke(request: Request): Response =
        Response(OK).body(users.joinToString(", "))
}
val userHandler = UserListerhandler(listOf("Иван", "Марья"))
// Констуктор в виде функции-генератора
fun messageResponder(message:String): HttpHandler = {
    Response(OK).body(message)
}
val messageHandler = messageResponder("Привет!")

Инфиксные функции в Kotlin #

Функция может быть помечена ключевым словом infix, если

  • Функция является членом другой функции или расширением
  • Функция принимает только один параметр
  • Параметр не описывает произвольное количество агументов
  • Параметр не должен иметь значения по умолчанию

Описание инфиксной функции и её использование

infix fun Int.shl(x: Int): Int { /*...*/ }
1 shl 2
1.shl(2)

Обязательно указывать как получателя, так и параметр


Описание маршрутизатора #

Основная задача сервера — обработка запросов от клиента. В рамках HTTP-протокола клиент формирует запрос к серверу и указывает к документу

При разработке сервера необходимо определить связь между маршрутом и обработчиком

Функция routes позволяет описать данную связь:

val handlerOne: HttpHandler = { Response(OK).body("First response") }
val handlerTwo: HttpHandler = { Response(OK).body("Second response") }
val app = routes (
    "first" bind GET to handlerOne,
    "second".bind(GET).to(handlerTwo),
)
  • handler1 и handler2 являются обработчиками, HttpHandler
  • Конфигурация обрабатывает пути /first и /second
  • Данный обработчик вернёт ответ с кодом 404, если для указанного пути нет обработчика

Описание связи маршрута и обработчика #

routes(
    "bob" bind GET to { Response(OK).body("you GET bob") },
    "rita" bind POST to { Response(OK).body("you POST rita") },
    "sue" bind DELETE to { Response(OK).body("you DELETE sue") },
)
  1. Описание маршрута с помощью шаблонной строки
  2. Вызов инфиксной функции bind связывает строку с методом по его обработке, возвращая объект типа PathMethod
  3. Указание названия HTTP-метода, который надо обработать
  4. Инфиксная функция PathMethod.to связывает результат работы предыдущего метода с обработчиками запроса, порождая объект типа RoutingHttpHandler
  5. Передача обработчика HttpHandler в качестве обработчика маршрута

Сформированный список объектов RoutingHttpHandler передаётся на вход функции routes, которая сама возвращает обработчик RoutingHttpHandler

Поддерживаемые HTTP-методы #

http4k поддерживает следующие HTTP-методы: GET, POST, PUT, DELETE, OPTIONS, TRACE, PATCH, PURGE, HEAD. Методы описаны в перечислении org.http4k.core.Method


Вложенные маршруты #

Функции routes в качестве аргумента можно передать объект RoutingHttpHandler, созданный в результаты другого вызова функции routes:

val webCourseRouter = routes(
    "topic" bind GET to { Response(OK).body("Веб-разработка") },
    "length" bind GET to { Response(OK).body("1 семестр") }
)
val unixCourseRouter = routes(
    "topic" bind GET to { Response(OK).body("Использование UNIX") },
    "length" bind GET to { Response(OK).body("1 или 2 семсетра")},
)
val coursesApp = routes(
    "test/ping" bind GET to { Response(OK).body("pong") },
    "unix" bind unixCourseRouter,
    "web" bind webCourseRouter,
)

Обрабатываются маршруты:

  • /test/ping
  • /unix/topic, /unix/length
  • /web/topic, /web/length

Динамические маршруты #

В рамках строки-шаблона могут содержаться переменные, которые можно использовать в рамках обработки запроса

routes (
    "/book/{title}" bind GET to { request ->
        Response.invoke(Status.OK).body(request.path("title").orEmpty())
    },
    "/author/{name}/latest" bind GET to { request ->
        Response.invoke(Status.OK).body(request.path("name").orEmpty())
    },
)
  • Шаблоны могут располагаться в любой части строки, их может быть несколько
  • Для получения переданных данных из пути используется метод Request.path
    fun Request.path(name: String): String?
  • Метод String?.orEmpty() возвращает пустую строку, если ссылка содержит null

Раздача статических данных #

Для отображения HTML-документов необходимо предоставить пользователю возможность получить CSS и JavaScript-документы. Для этого используется функция static

Предположим, что базовым пакетом приложения является org.example, а в рамках ресурсов в пакете org.example.public находятся статические данные, то для их показа используем метод Classpath класса ResourceLoader

routes(
    static(ResourceLoader.Classpath("/org/example/public")),
)

Если данные находятся на жёстком диске, то необходимо использовать загрузчик с жёсткого диска, метод Directory

routes(
    static(ResourceLoader.Directory("../uploads")
)

Архитектурный взгляд на приложение #

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

Компоненты приложения:

  • Маршрутизатор HTTP-запросов
  • Обработчики HTTP-запросов
  • Реализация классов предметной области
  • Шаблонизаторы

Все компоненты соединяются между собой внутри маршрутизатора

© A. M. Васильев, 2024, CC BY-SA 4.0, andrey@crafted.su