Найти тему

HTTP-запросы с использованием Kotlin и http

Оглавление

1. Введение

Протокол HTTP и API, построенные на его основе, в наши дни имеют первостепенное значение в программировании.

В JVM у нас есть несколько доступных вариантов, от библиотек низкого уровня до библиотек очень высокого уровня, от уже существующих проектов до начинающих разработчиков. Однако большинство из них ориентированы в первую очередь на Java-программы.

В этой статье
мы рассмотрим khttp, идиоматическую библиотеку Kotlin для использования ресурсов и API на основе HTTP.

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

2. Зависимости

Чтобы использовать библиотеку в нашем проекте, сначала мы должны добавить ее в наши зависимости. Хотя исходный артефакт больше недоступен, сообщество опубликовало его в Maven Central:

<dependency>
<groupId>org.danilopianini</groupId>
<artifactId>khttp</artifactId>
<version>1.3.1</version>
</dependency>

3. Основное использование

Основы протокола HTTP просты, хотя мелкие детали могут быть довольно сложными. Таким образом, http также имеет простой интерфейс.

Для каждого HTTP-метода в http-пакете мы можем найти функцию пакетного уровня, такую как get, post и так далее.

Все функции принимают один и тот же набор аргументов и возвращают объект Response; мы подробно рассмотрим их в следующих разделах.

В ходе этой статьи мы будем использовать полную форму, например, khttp.put. В наших проектах мы, конечно, можем импортировать и, возможно, переименовывать эти методы:

import khttp.delete as httpDelete

Примечание: мы добавили объявления типов для наглядности в примерах кода, потому что без IDE им было бы трудно следовать.

4. Простой запрос

Каждый HTTP-запрос содержит как минимум два обязательных компонента: метод и URL-адрес. В khttp метод определяется вызываемой функцией, как мы видели в предыдущем разделе.

URL-адрес является единственным обязательным аргументом для метода; таким образом, мы можем легко выполнить простой запрос:

khttp.get("http://httpbin.org/get")

В следующих разделах мы рассмотрим все запросы на предмет их успешного выполнения.

4.1. Добавление параметров

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

Методы khttp принимают аргумент params, который представляет собой отображение пар ключ-значение для включения в строку запроса:

khttp.get(
url = "http://httpbin.org/get",
params = mapOf("key1" to "value1", "keyn" to "valuen"))

Обратите внимание, что мы использовали функцию mapOf для создания карты "на лету"; результирующий URL-адрес запроса будет:

http://httpbin.org/get?key1=value1&keyn=valuen

5. Орган запроса

Еще одна распространенная операция, которую нам часто приходится выполнять, - это отправка данных, обычно в качестве полезной нагрузки запроса POST или PUT.

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

5.1. Отправка полезной нагрузки в формате JSON

Мы можем использовать аргумент json для отправки объекта или массива JSON. Он может быть нескольких разных типов:

  • JSONObject или JSONArray, как это предусмотрено библиотекой org.json
  • Map, которая преобразуется в объект JSON
  • Collection, повторяемая или массив, который преобразуется в массив JSON

Мы можем легко превратить наш предыдущий пример GET в POST, который отправит простой объект JSON:

khttp.post(
url = "http://httpbin.org/post",
json = mapOf("key1" to "value1", "keyn" to "valuen"))

Обратите внимание, что преобразование коллекций в объекты JSON является поверхностным. Например, список объектов Map не будет преобразован в массив объектов JSON в формате JSON, а скорее в массив строк.

Для глубокого преобразования нам понадобится более сложная библиотека отображения JSON, такая как Jackson. Возможности преобразования в библиотеке предназначены только для простых случаев.

5.2. Отправка данных формы (URL в кодировке)

Для отправки данных формы (закодированных по URL-адресу, как в HTML-формах) мы используем аргумент data с map:

khttp.post(
url = "http://httpbin.org/post",
data = mapOf("key1" to "value1", "keyn" to "valuen"))

5.3. Загрузка файлов (составная форма)

Мы можем отправить один или несколько файлов, закодированных в виде запроса данных в виде составной формы.

В этом случае мы используем аргумент files:

khttp.post(
url = "http://httpbin.org/post",
files = listOf(
FileLike("file1", "content1"),
FileLike("file2", File("kitty.jpg"))))

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

5.4. Отправка необработанного контента

Если ни один из вышеперечисленных вариантов не подходит, мы можем использовать InputStream для отправки необработанных данных в виде тела HTTP-запроса:

khttp.post(url = "http://httpbin.org/post", data = someInputStream)

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

6. Обработка ответа

До сих пор мы рассматривали различные способы отправки данных на сервер. Но многие HTTP-операции полезны и из-за данных, которые они возвращают.

http основан на блокировании ввода-вывода,
поэтому все функции, соответствующие HTTP-методам, возвращают объект Response, содержащий ответ, полученный от сервера.

Этот объект обладает различными свойствами, к которым мы можем получить доступ, в зависимости от типа содержимого.

6.1. Ответы в формате JSON

Если мы знаем, что ответ представляет собой объект JSON или массив, мы можем использовать свойства JSONObject и JSONArray:

val response : Response = khttp.get("http://httpbin.org/get")
val obj : JSONObject = response.jsonObject
print(obj["someProperty"])

6.2. Текстовые или двоичные ответы

Если мы хотим прочитать ответ в виде строки, мы можем использовать свойство text:

val message : String = response.text

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

val imageData : ByteArray = response.content

Наконец, мы также можем получить доступ к базовому входному потоку:

val inputStream : InputStream = response.raw

7. Расширенное использование

Давайте также рассмотрим несколько более продвинутых шаблонов использования, которые в целом полезны и которые мы еще не рассматривали в предыдущих разделах.

7.1. Обработка заголовков и файлов cookie

Все http-функции принимают аргумент headers, который представляет собой отображение имен и значений заголовков.

val response = khttp.get(
url = "http://httpbin.org/get",
headers = mapOf("header1" to "1", "header2" to "2"))

Аналогично и с файлами cookie:

val response = khttp.get(
url = "http://httpbin.org/get",
cookies = mapOf("cookie1" to "1", "cookie2" to "2"))

Мы также можем получить доступ к заголовкам и файлам cookie, отправленным сервером в ответе:

val contentType : String = response.headers["Content-Type"]
val sessionID : String = response.cookies["JSESSIONID"]

7.2. Обработка ошибок

В HTTP могут возникать ошибки двух типов: ответы об ошибках, такие как 404 – Не найдено, которые являются частью протокола; и ошибки низкого уровня, такие как “отказано в подключении”.

Первый тип не приводит к тому, что khttp генерирует исключения; вместо этого
мы должны проверить свойство Response StatusCode:

val response = khttp.get(url = "http://httpbin.org/nothing/to/see/here")
if(response.statusCode == 200) {
process(response)
} else {
handleError(response)
}

Ошибки более низкого уровня, напротив, приводят к возникновению исключений из базовой подсистемы ввода-вывода Java, таких как ConnectException.

7.3. Потоковая передача ответов

Иногда сервер может отправить в ответ большой объем контента и/или для ответа требуется много времени. В таких случаях мы можем захотеть обработать ответ по частям, а не ждать его завершения и занимать память.

Если мы хотим попросить библиотеку выдать нам потоковый ответ, то мы должны передать true в качестве аргумента stream:

val response = khttp.get(url = "http://httpbin.org", stream = true)

Затем мы можем обрабатывать его по частям:

response.contentIterator(chunkSize = 1024).forEach { arr : ByteArray -> handleChunk(arr) }

7.4. Нестандартные методы

В том маловероятном случае, когда нам понадобится использовать HTTP–метод (или глагол), который khttp не предоставляет изначально, – скажем, для какого-либо расширения протокола HTTP, такого как WebDAV, - мы все равно справимся.

Фактически, все функции в http-пакете, которые соответствуют HTTP-методам, реализованы с использованием универсальной функции запроса, которую мы тоже можем использовать:

khttp.request(
method = "COPY",
url = "http://httpbin.org/get",
headers = mapOf("Destination" to "/copy-of-get"))

7.5. Другие функции

Мы не затронули все возможности khttp. Например, мы не обсуждали тайм-ауты, перенаправления и историю, а также асинхронные операции.

Официальная документация является основным источником информации о библиотеке и всех ее функциях.

8. Заключение

В этом руководстве мы рассмотрели, как отправлять HTTP-запросы в Kotlin с помощью идиоматической библиотеки khttp.

Оригинал статьи: https://www.baeldung.com/kotlin/khttp