HTTP заявки с Kotlin и khttp

1. Въведение

Протоколът HTTP и API, изградени върху него, са от централно значение в програмирането в наши дни.

На JVM имаме няколко налични опции, от библиотеки от по-ниско до много високо ниво, от установени проекти до нови деца в блока. Повечето от тях обаче са насочени предимно към Java програми.

В тази статия ще разгледаме khttp, идиоматична библиотека на Kotlin за консумация на HTTP-базирани ресурси и API.

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

За да използваме библиотеката в нашия проект, първо трябва да я добавим към нашите зависимости:

 khttp khttp 0.1.0 

Тъй като това все още не е в Maven Central, трябва да активираме и хранилището на JCenter:

 central //jcenter.bintray.com 

Версия 0.1.0 е текущата към момента на писане. Можем, разбира се, да проверим JCenter за по-нов.

3. Основна употреба

Основите на HTTP протокола са прости, въпреки че фините детайли могат да бъдат доста сложни. Следователно khttp има и прост интерфейс.

За всеки HTTP метод можем да намерим функция на ниво пакет в пакета khttp , като get, post и т.н.

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

В хода на тази статия ще използваме напълно квалифицираната форма, например khttp.put . В нашите проекти можем, разбира се, да импортираме и евентуално да преименуваме тези методи:

import khttp.delete as httpDelete

Забележка: Добавили сме декларации за типове за яснота в примери за кодове, защото без IDE може да е трудно да се следват.

4. Проста заявка

Всяка HTTP заявка има поне два задължителни компонента: метод и URL адрес . В khttp методът се определя от функцията, която извикваме, както видяхме в предишния раздел.

URL адресът е единственият задължителен аргумент за метода; така че можем лесно да изпълним проста заявка:

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

В следващите раздели ще разгледаме всички заявки, за да завършим успешно.

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

Често трябва да предоставяме параметри на заявката в допълнение към основния URL адрес, особено за GET заявки.

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

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

Забележете, че сме използвали функцията mapOf, за да изградим карта в движение; полученият URL адрес на заявката ще бъде:

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

5. Орган за искане

Друга често срещана операция, която често трябва да изпълняваме, е изпращането на данни, обикновено като полезен товар на POST или PUT заявка.

За това библиотеката предлага няколко опции, които ще разгледаме в следващите раздели.

5.1. Изпращане на JSON полезен товар

Можем да използваме аргумента json , за да изпратим JSON обект или масив. Тя може да бъде от няколко различни вида:

  • А JSON обектите или JSONArray както е предвидено от библиотеката org.json
  • А карта , която се превръща в обект JSON
  • A Collection , Iterable или масив, който се трансформира в JSON масив

Можем лесно да превърнем нашия по-ранен пример GET в POST, който ще изпрати прост JSON обект:

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

Имайте предвид, че преобразуването от колекции в JSON обекти е плитко. Например, Списък на картата няма да бъде преобразуван в JSON масив от JSON обекти, а по-скоро в масив от низове.

За дълбоко преобразуване ще ни е необходима по-сложна JSON картографираща библиотека като Jackson. Устройството за преобразуване на библиотеката е предназначено само за прости случаи.

5.2. Изпращане на данни от формуляра (кодиран URL)

За да изпратим данни от формуляри (URL кодирани, както в HTML формуляри), използваме аргумента за данни с Map :

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

5.3. Качване на файлове (многочастен формуляр)

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

В този случай използваме аргумента files :

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

Виждаме, че khttp използва абстракция FileLike , която е обект с име и съдържание. Съдържанието може да бъде низ, байтов масив, файл или път .

5.4. Изпращане на необработено съдържание

Ако нито една от опциите по-горе не е подходяща, можем да използваме InputStream за изпращане на сурови данни като тяло на HTTP заявка:

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

В този случай най-вероятно ще трябва да зададем ръчно и някои заглавки, които ще разгледаме в следващ раздел.

6. Handling the Response

So far we've seen various ways of sending data to a server. But many HTTP operations are useful because of the data they return as well.

khttp is based on blocking I/O, therefore all functions corresponding to HTTP methods return a Response object containing the response received from the server.

This object has various properties that we can access, depending on the type of content.

6.1. JSON Responses

If we know the response to be a JSON object or array, we can use the jsonObject and jsonArray properties:

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

6.2. Text or Binary Responses

If we want to read the response as a String instead, we can use the text property:

val message : String = response.text

Or, if we want to read it as binary data (e.g. a file download) we use the content property:

val imageData : ByteArray = response.content

Finally, we can also access the underlying InputStream:

val inputStream : InputStream = response.raw

7. Advanced Usage

Let's also take a look at a couple of more advanced usage patterns which are generally useful, and that we haven't yet treated in the previous sections.

7.1. Handling Headers and Cookies

All khttp functions take a headers argument which is a Map of header names and values.

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

Similarly for cookies:

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

We can also access headers and cookies sent by the server in the response:

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

7.2. Handling Errors

There are two types of errors that can arise in HTTP: error responses, such as 404 – Not Found, which are part of the protocol; and low-level errors, such as “connection refused”.

The first kind doesn't result in khttp throwing exceptions; instead, we should check the Response statusCode property:

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

Lower-level errors, instead, result in exceptions being thrown from the underlying Java I/O subsystem, such as ConnectException.

7.3. Streaming Responses

Sometimes the server can respond with a big piece of content, and/or take a long time to respond. In those cases, we may want to process the response in chunks, rather than waiting for it to complete and take up memory.

If we want to instruct the library to give us a streaming response, then we have to pass true as the stream argument:

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

Then, we can process it in chunks:

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

7.4. Non-Standard Methods

In the unlikely case that we need to use an HTTP method (or verb) that khttp doesn't provide natively – say, for some extension of the HTTP protocol, like WebDAV – we're still covered.

In fact, all functions in the khttp package, which correspond to HTTP methods, are implemented using a generic request function that we can use too:

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

7.5. Other Features

We haven't touched all the features of khttp. For example, we haven't discussed timeouts, redirects and history, or asynchronous operations.

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

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

В този урок видяхме как да правим HTTP заявки в Kotlin с идиоматичната библиотека khttp.

Изпълнението на всички тези примери може да бъде намерено в проекта GitHub.