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