HTTP-запросы с Kotlin и KHTTP
1. Вступление
Протокол HTTP и API-интерфейсы, построенные на нем, сегодня играют центральную роль в программировании.
На JVM у нас есть несколько доступных опций, от низкоуровневых до очень высокоуровневых библиотек, от устоявшихся проектов до новых детей в блоке. Тем не менее, большинство из них ориентированы в первую очередь на Java-программы.
В этой статьеwe’re going to look at khttp, an idiomatic Kotlin library for consuming HTTP-based resources и API.
2. зависимости
Чтобы использовать библиотеку в нашем проекте, сначала мы должны добавить ее в наши зависимости:
khttp
khttp
0.1.0
Поскольку это еще не сделано в Maven Central, мы также должны включить репозиторий JCenter:
central
http://jcenter.bintray.com
Версия 0.1.0 является текущей на момент написания. Мы, конечно, можемcheck JCenter for a newer one.
3. Основное использование
Основы протокола HTTP просты, хотя мелкие детали могут быть довольно сложными. Следовательно, у khttp простой интерфейс.
For every HTTP method, we can find a package-level function in the khttp package, напримерget,post и так далее.
Все функции принимают один и тот же набор аргументов и возвращают объектResponse; мы увидим подробности этого в следующих разделах.
В этой статье мы будем использовать полную форму, например,khttp.put. В наших проектах мы можем, конечно, импортировать и, возможно, переименовать эти методы:
import khttp.delete as httpDelete
Note: we’ve added type declarations for clarity throughout code examples, потому что без IDE за ними будет сложно следить.
4. Простая просьба
Every HTTP request has at least two required components: a method and a URL. В khttp метод определяется вызываемой функцией, как мы видели в предыдущем разделе.
URL-адрес является единственным обязательным аргументом для метода; Итак, мы можем легко выполнить простой запрос:
khttp.get("http://httpbin.org/get")
В следующих разделах мы будем считать все запросы успешно выполненными.
4.1. Добавление параметров
Нам часто приходится предоставлять параметры запроса в дополнение к базовому URL, особенно для запросов GET.
khttp’s methods accept a params argument, который представляет собойMap пар "ключ-значение" для включения в запросString:
khttp.get(
url = "http://httpbin.org/get",
params = mapOf("key1" to "value1", "keyn" to "valuen"))
Обратите внимание, что мы использовали функциюmapOf для созданияMap на лету; результирующий URL-адрес запроса будет:
http://httpbin.org/get?key1=value1&keyn=valuen
5. Тело запроса
Другая распространенная операция, которую нам часто приходится выполнять, - это отправка данных, обычно в виде полезных данных запроса POST или PUT.
Для этого библиотека предлагает несколько вариантов, которые мы рассмотрим в следующих разделах.
5.1. Отправка полезной нагрузки JSON
We can use the json argument to send a JSON object or array. Может быть нескольких типов:
-
JSONObject илиJSONArray, предоставленные библиотекой org.json
-
AMap, который преобразуется в объект JSON
-
Collection,Iterable или массив, который преобразуется в массив JSON
Мы можем легко превратить наш предыдущий пример GET в POST, который отправит простой объект JSON:
khttp.post(
url = "http://httpbin.org/post",
json = mapOf("key1" to "value1", "keyn" to "valuen"))
Обратите внимание, что преобразование из коллекций в объекты JSON невелико. Например,List из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"))))
Мы видим, что khttp использует абстракциюFileLike, которая представляет собой объект с именем и содержанием. Содержимое может быть строкой, байтовым массивом,File илиPath.
5.4. Отправка необработанного содержимого
Если ни один из вышеперечисленных вариантов не подходит, мы можем использоватьInputStream для отправки необработанных данных в качестве тела HTTP-запроса:
khttp.post(url = "http://httpbin.org/post", data = someInputStream)
В этом случае нам, скорее всего, придется вручную установить и некоторые заголовки, о чем мы поговорим в следующем разделе.
6. Обработка ответа
До сих пор мы видели различные способы отправки данных на сервер. Но многие операции HTTP полезны из-за данных, которые они также возвращают.
khttp основан на блокировке ввода-вывода, поэтомуall functions corresponding to HTTP methods return a Response object содержит ответ, полученный от сервера.
Этот объект имеет различные свойства, к которым мы можем получить доступ, в зависимости от типа содержимого.
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. Текстовые или двоичные ответы
Если мы хотим вместо этого читать ответ какString, мы можем использовать свойствоtext:
val message : String = response.text
Или, если мы хотим прочитать это как двоичные данные (например, загрузка файла) используем свойствоcontent:
val imageData : ByteArray = response.content
Наконец, мы также можем получить доступ к базовомуInputStream:
val inputStream : InputStream = response.raw
7. Расширенное использование
Давайте также рассмотрим пару более сложных шаблонов использования, которые в целом полезны и которые мы еще не рассматривали в предыдущих разделах.
7.1. Обработка заголовков и файлов cookie
All khttp functions take a headers argument, который представляет собойMap имен и значений заголовков.
val response = khttp.get(
url = "http://httpbin.org/get",
headers = mapOf("header1" to "1", "header2" to "2"))
Аналогично для печенья:
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 - Not Found, которые являются частью протокола; и ошибки низкого уровня, такие как «отказано в соединении».
Первый тип не приводит к исключениям, генерирующим khttp; вместо этогоwe should check the Response statusCode property:
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, - мы все еще покрыты.
Фактически, все функции в пакете khttp, которые соответствуют HTTP-методам, реализованы с использованием общей функцииrequest, которую мы тоже можем использовать:
khttp.request(
method = "COPY",
url = "http://httpbin.org/get",
headers = mapOf("Destination" to "/copy-of-get"))
7.5. Другие преимущества
Мы не затронули все возможности khttp. Например, мы не обсуждали тайм-ауты, перенаправления, историю или асинхронные операции.
The official documentation - это основной источник информации о библиотеке и всех ее функциях.
8. Заключение
В этом руководстве мы увидели, как выполнять HTTP-запросы в Kotlin с идиоматической библиотекой khttp.
Реализацию всех этих примеров можно найти вthe GitHub project - это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.