Перейти к основному содержимому

Ktor Client — HTTP-запросы

Разработчику Архитектору

Ktor Client

Сервер Ktor принимает HTTP-запросы. Ktor Client — библиотека, которая эти запросы отправляет из приложения: Desktop, Android, backend-утилита. Оба используют корутины: вызовы get / postsuspend-функции.

Типичная цепочка: ViewModel → HttpClient → JSON → список на экране (Compose, Flow).


Словарь терминов

ТерминПростыми словами
HTTP-клиентПрограмма, которая формирует запрос (метод, URL, заголовки, тело) и читает ответ.
GET«Дай данные» — обычно без тела.
POST«Создай / отправь данные» — часто с JSON в теле.
HttpClientГлавный объект Ktor Client; создаётся один раз на приложение.
Engine (CIO, OkHttp)Низкоуровневая реализация сокетов под платформу.
Content NegotiationАвтоматический JSON ↔ Kotlin-объект.
Suspend-вызовclient.get(...) можно вызывать только из корутины.

Зависимости (JVM)

build.gradle.kts:

plugins {
kotlin("jvm") version "1.9.24"
kotlin("plugin.serialization") version "1.9.24"
application
}

dependencies {
implementation("io.ktor:ktor-client-core:2.3.12")
implementation("io.ktor:ktor-client-cio:2.3.12")
implementation("io.ktor:ktor-client-content-negotiation:2.3.12")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
implementation("io.ktor:ktor-client-logging:2.3.12")
}

На Android вместо cio часто подключают ktor-client-okhttp.


Модель данных

Те же классы, что на сервере в 221.md:

import kotlinx.serialization.Serializable

@Serializable
data class Note(val id: Int, val text: String)

@Serializable
data class NoteCreate(val text: String)

Создание клиента

import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json

val client = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
install(Logging) {
level = LogLevel.INFO
}
}
НастройкаЗачем
ContentNegotiation + json()Тело ответа → Note, объект → JSON в POST
ignoreUnknownKeys = trueСервер добавил поле — клиент со старой моделью не падает
LoggingВ консоль: URL, статус, время (отладка)

Важно: HttpClient дорогой в создании — один экземпляр на приложение (singleton, DI). При завершении: client.close().


GET и POST — разбор

Предположим, сервер из 221.md слушает http://127.0.0.1:8080.

import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.coroutines.runBlocking

suspend fun demo(client: HttpClient) {
val health: Map<String, String> = client.get("http://127.0.0.1:8080/health").body()
println(health)

val notes: List<Note> = client.get("http://127.0.0.1:8080/notes").body()
println("Заметок: ${notes.size}")

val created: Note = client.post("http://127.0.0.1:8080/notes") {
contentType(ContentType.Application.Json)
setBody(NoteCreate("Из клиента"))
}.body()
println("Создано: ${created.id}${created.text}")
}

fun main() = runBlocking {
val client = HttpClient(CIO) {
install(ContentNegotiation) { json() }
}
try {
demo(client)
} finally {
client.close()
}
}

Построчно:

СтрокаЧто происходит
client.get(url)HTTP GET, suspend до ответа
.body<Map<...>>()Десериализация JSON в тип
post(url) { ... }Блок настраивает запрос
contentType(Application.Json)Заголовок Content-Type: application/json
setBody(NoteCreate(...))Сериализация объекта в JSON-тело
runBlockingТолько для main; в Android — viewModelScope.launch

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

import io.ktor.client.plugins.*

suspend fun safeGet(client: HttpClient, url: String): String? =
try {
client.get(url).body()
} catch (e: ClientRequestException) {
println("HTTP ${e.response.status}: ${e.response.status.description}")
null
} catch (e: ServerResponseException) {
println("Ошибка сервера: ${e.response.status}")
null
}
ИсключениеКогда
ClientRequestException4xx — ошибка запроса (404, 401)
ServerResponseException5xx — ошибка сервера

Таймаут в блоке клиента:

HttpClient(CIO) {
engine {
requestTimeout = 10_000
}
}

Заголовки и токен

client.get("https://api.example.com/profile") {
header(HttpHeaders.Authorization, "Bearer $token")
parameter("page", 1)
}

parameter добавляет ?page=1 к URL. Плагин Auth может подставлять токен автоматически для серии запросов.


Android и Kotlin Multiplatform

ПлатформаДвижок
JVM / DesktopCIO
AndroidOkHttp
iOSDarwin

В KMP: expect fun createHttpClient(): HttpClient в commonMain, actual в androidMain / iosMain — сеть в общем коде, движок под платформу.


Частые ошибки

СимптомПричина
Connection refusedСервер не запущен
Serialization errorНет @Serializable или разные поля JSON
Вызов из UI-потока без корутиныНужен launch + suspend
УтечкиКлиент не close() при выходе из процесса

Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).