Основы обработки HTTP-запросов

Основы обработки HTTP-запросов #

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

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


Архитектура серверных http4k-приложений #

diagram

  • http4k позволяет использовать несколько разных реализаций HTTP-серверов
  • Каждую библиотеку можно настроить для оптимального быстродействия
  • Логика обработки запросов описывается функциональным типом HttpHandler
  • Для улучшения производительности функциональный тип вызывается на нескольких потоках исполнения
  • Библиотека HTTP-сервера тоже работает в многопоточном режиме

Maven-зависимости #

  • http4k состоит из множества компонентов, которые взаимодействуют между собой и интегрируются с внешними компонентами
  • Для реализации сервера необходимо подключить ядро библиотеки и реализацию сервера:
    • Ядро: "org.http4k:http4k-core:VERSION"
    • Сервер Netty: "org.http4k:http4k-server-netty:VERSION"

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

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

Простейший HTTP-сервер #

typealias HttpHandler = (Request) -> Response

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

val app: HttpHandler = // ...
val jettyServer = app.asServer(Netty(9000)).start()
  • После запуска данное приложение будет доступно по адресу http://localhost:9000
  • Конфигурация сервера описывается с помощью интерфейса ServerConfig
  • Настройки по умолчанию можно попытаться улучшить, если выполнить объективные замеры производительности

Описание 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 является неизменяемым
  • Большинство методов внутри себя вызывают метод copy классов данных

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

http4k предлагает класс org.http4k.core.Status, описывающий статус ответа и предоставляющий константы для описания стандартных статусов

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

  • HTTP-код: 200
  • Назначение: запрос был обработан, получен корректный результат

Status.BAD_REQUEST, некорректный зпрос #

  • HTTP-код: 400
  • Назначение: запрос невозможно обработать ввиду наличия в нём проблем

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

  • HTTP код: 404
  • Назначение: запрос к несуществующему на сервере документу

Тело ответа #

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

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

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

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

Указание типа документа #

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

  • Получение документа со списком других документов
  • Получение документов с детальным описанием элементов
  • Получение документов со сводкой по элементам системы

Клиент может

  • предполагать какой тип данных вернёт ему сервер
  • автоматически определять тип данных
  • ориентироваться на стандартный заголовок Content-Type

Сервер не может своим ответом повлиять на логику обработки ответа на стороне клиента


Указание типа документа в http4k #

Наши сервера будут сообщать пользователям тип документа с помощью Content-Type

Значением заголовка Content-Type является набор из следующих параметров:

  • MIME-тип документа, media-type
  • кодировка, charset
  • директива составных сущностей, boundary

Возможные корректные варианты:

  • text/javascript; encoding=utf-8
  • text/html; charset=utf-8
  • application/json; charset=utf-8

Установка заголовка ответа напрямую #

HttpMessage, и соответственно Response, предоставляет функцию header:

fun header(name: String, value: String?): HttpMessage

val response = Response(Status.OK)
    .header("Content-Type", "application/json; charset=utf-8")
     .body("{\"hello\": \"world\"}")

Использование специальной подсистемы http4k #

  • http4k предоставляет удобный класс org.http4k.core.ContentType
  • И набор констант для описания разных типов содержимого: APPLICATION_JSON, TEXT_HTML и т.д.
  • Данным классом можно воспользоваться с помощью функции-расширения org.http4k.lens.contentType
val response = Response(Status.OK)
    .contentType(ContentType.APPLICATION_JSON)
    .body("{\"hello\": \"world\"}")

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