Ktor Client для Kotlin: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Ktor Client для Kotlin: полное руководство с примерами

Ktor Client — это мощная, легковесная и мультиплатформенная библиотека для выполнения HTTP-запросов на языке Kotlin. Разработанная компанией JetBrains, она является частью экосистемы Ktor и предоставляет асинхронный, корутино-ориентированный API для взаимодействия с веб-серверами. В отличие от многих других HTTP-клиентов, Ktor Client изначально спроектирован для работы на нескольких платформах: JVM, Android, iOS, macOS, Linux, Windows и JavaScript (через Kotlin/JS). Это делает его идеальным выбором для разработки кроссплатформенных приложений на Kotlin Multiplatform (KMP).

Зачем нужен Ktor Client? В современной разработке практически ни одно приложение не обходится без взаимодействия с удалёнными API. Будь то загрузка данных из REST-сервиса, отправка форм, загрузка файлов или работа с WebSockets — HTTP-клиент является фундаментальным инструментом. Ktor Client предлагает не просто базовые возможности GET и POST запросов, а целую экосистему плагинов (или "движков"), которые позволяют настраивать сериализацию (JSON, XML, Protobuf), сжатие, логирование, аутентификацию, повторные попытки и многое другое. Его главное преимущество — полная асинхронность на основе корутин Kotlin, что обеспечивает высокую производительность и лаконичный код без callback-ов.

Библиотека отличается модульной архитектурой. Вы подключаете только те компоненты, которые реально нужны вашему проекту: базовый клиент, конкретный движок (например, OkHttp для Android/JVM или Darwin для iOS), сериализатор и необходимые плагины. Это минимизирует размер итогового приложения и избегает конфликтов зависимостей. Ktor Client активно развивается, имеет отличную документацию и поддерживается сообществом Kotlin.

Установка

Ktor Client распространяется через систему сборки Gradle (или Maven). Поскольку это не Python-пакет, команда pip install не используется. Ниже приведены точные инструкции для добавления зависимостей в ваш build.gradle.kts файл.

Важно: Версии библиотек должны быть совместимы. Рекомендуется использовать последнюю стабильную версию из официального репозитория Maven Central. На момент написания статьи актуальная версия — 2.3.12.

// build.gradle.kts (Gradle Kotlin DSL)

// 1. Добавьте репозиторий (обычно уже есть)
repositories {
    mavenCentral()
}

// 2. Добавьте зависимости в блок dependencies
dependencies {
    // Базовый клиент (обязательно)
    implementation("io.ktor:ktor-client-core:2.3.12")

    // Движок (Engine) - выберите один из вариантов:
    // Для JVM (использует Java HttpClient или OkHttp)
    implementation("io.ktor:ktor-client-okhttp:2.3.12") // Рекомендуется для Android/JVM
    // implementation("io.ktor:ktor-client-java:2.3.12") // Альтернатива для JVM

    // Для iOS/macOS (нативный Darwin)
    // implementation("io.ktor:ktor-client-darwin:2.3.12")

    // Для JavaScript
    // implementation("io.ktor:ktor-client-js:2.3.12")

    // Для Windows/Linux (Curl)
    // implementation("io.ktor:ktor-client-curl:2.3.12")

    // Плагины (опционально, но очень полезно):
    // Сериализация JSON (Gson или Kotlinx Serialization)
    implementation("io.ktor:ktor-client-content-negotiation:2.3.12")
    implementation("io.ktor:ktor-serialization-gson:2.3.12")
    // или implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")

    // Логирование
    implementation("io.ktor:ktor-client-logging:2.3.12")

    // Для работы с WebSockets
    // implementation("io.ktor:ktor-client-websockets:2.3.12")
}

Если вы используете Maven, зависимости будут выглядеть аналогично. После добавления зависимостей, синхронизируйте проект (Sync Project with Gradle Files).

Быстрый старт: минимальный рабочий пример

Давайте напишем простейшую программу, которая выполняет GET-запрос к публичному API и выводит ответ в консоль. Этот пример демонстрирует базовую структуру работы с Ktor Client.

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // 1. Создаем HTTP-клиент
    val client = HttpClient()

    // 2. Выполняем GET-запрос
    val response: HttpResponse = client.get("https://api.github.com")

    // 3. Читаем тело ответа как строку
    val body: String = response.bodyAsText()

    // 4. Выводим результат
    println("Статус: ${response.status}")
    println("Тело ответа (первые 200 символов): ${body.take(200)}")

    // 5. Обязательно закрываем клиент для освобождения ресурсов
    client.close()
}

Объяснение:

  • HttpClient() — создает экземпляр клиента. Если не указан движок, Ktor попытается автоматически выбрать подходящий для вашей платформы.
  • client.get(...) — выполняет асинхронный GET-запрос. Возвращает HttpResponse.
  • response.bodyAsText() — преобразует тело ответа в строку.
  • runBlocking — используется только в main-функции для запуска корутины. В реальных приложениях (Android, Spring) вы будете использовать другие корутин-билдеры (launch, async).
  • client.close() — критически важно закрывать клиент, когда он больше не нужен, чтобы избежать утечки ресурсов (соединений, потоков).

Основные методы, функции и классы

Ktor Client предоставляет богатый API. Рассмотрим 8 ключевых элементов, которые вы будете использовать чаще всего.

1. HttpClient (класс)

Сигнатура: class HttpClient(engine: HttpClientEngine? = null, block: HttpClientConfig<*>.() -> Unit = {})

Что делает: Главный класс для выполнения HTTP-запросов. Конфигурируется с помощью лямбда-выражения, где можно установить движок, таймауты, плагины и другие настройки.

// Создание клиента с конфигурацией
val client = HttpClient(OkHttp) { // Указываем движок OkHttp
    install(ContentNegotiation) {
        json(Gson { // Настраиваем сериализацию JSON
            prettyPrint = true
            setPrettyPrinting()
        })
    }
    install(Logging) {
        level = LogLevel.BODY // Логируем все запросы и ответы
    }
    defaultRequest {
        url("https://api.example.com/") // Базовый URL для всех запросов
    }
}

2. client.get() (метод)

Сигнатура: suspend fun HttpClient.get(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse

Что делает: Выполняет HTTP GET-запрос. Позволяет добавлять заголовки, параметры запроса и другие настройки через builder.

val response: HttpResponse = client.get("https://api.github.com/users/octocat") {
    header("Accept", "application/vnd.github.v3+json")
    parameter("per_page", 10) // Добавляем query-параметр
}
println(response.bodyAsText())

3. client.post() (метод)

Сигнатура: suspend fun HttpClient.post(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse

Что делает: Выполняет HTTP POST-запрос. Используется для отправки данных на сервер (например, создание ресурса).

val response = client.post("https://jsonplaceholder.typicode.com/posts") {
    contentType(ContentType.Application.Json)
    setBody("""{"title": "foo", "body": "bar", "userId": 1}""")
}
println("Создан пост с ID: ${response.bodyAsText()}")

4. client.put() (метод)

Сигнатура: suspend fun HttpClient.put(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse

Что делает: Выполняет HTTP PUT-запрос. Обычно используется для полного обновления существующего ресурса.

val response = client.put("https://jsonplaceholder.typicode.com/posts/1") {
    contentType(ContentType.Application.Json)
    setBody("""{"id": 1, "title": "updated title", "body": "updated body", "userId": 1}""")
}
println(response.status)

5. client.delete() (метод)

Сигнатура: suspend fun HttpClient.delete(urlString: String, block: HttpRequestBuilder.() -> Unit = {}): HttpResponse

Что делает: Выполняет HTTP DELETE-запрос. Используется для удаления ресурса на сервере.

val response = client.delete("https://jsonplaceholder.typicode.com/posts/1")
println("Ресурс удален. Статус: ${response.status}")

6. HttpResponse (класс)

Сигнатура: class HttpResponse(val status: HttpStatusCode, val request: HttpRequest, val headers: Headers, val body: ByteReadChannel, ...)

Что делает: Представляет ответ сервера. Содержит статус, заголовки и тело ответа. Предоставляет методы для чтения тела в различных форматах.

val response: HttpResponse = client.get("https://httpbin.org/get")
println("Статус: ${response.status}") // HttpStatusCode
println("Заголовок Content-Type: ${response.headers["Content-Type"]}")
println("Длина тела: ${response.contentLength()}")

// Чтение тела разными способами
val textBody: String = response.bodyAsText()
val byteArray: ByteArray = response.body()
// Если настроен ContentNegotiation, можно десериализовать в объект:
// val myObject: MyDataClass = response.body()

7. HttpRequestBuilder (класс)

Сигнатура: class HttpRequestBuilder { var url: URLBuilder; var method: HttpMethod; var headers: HeadersBuilder; var body: OutgoingContent; ... }

Что делает: Строитель для конфигурации HTTP-запроса. Используется в лямбда-блоке методов get, post и т.д. Позволяет устанавливать URL, заголовки, тело, параметры и таймауты.

client.get("https://api.example.com/search") {
    // Настройка URL
    url {
        parameters["q"] = "kotlin"
        parameters["page"] = "1"
    }
    // Заголовки
    header("Authorization", "Bearer my_token")
    header("User-Agent", "KtorClient/2.0")
    // Таймауты
    timeout {
        requestTimeoutMillis = 5000
        connectTimeoutMillis = 3000
    }
}

8. ContentNegotiation (плагин)

Сигнатура: fun ContentNegotiationConfig.json(block: JsonBuilder.() -> Unit = {}) (и другие форматы)

Что делает: Плагин для автоматической сериализации и десериализации тела запроса/ответа. Поддерживает JSON (Gson, Kotlinx Serialization), XML и другие форматы. Позволяет работать с объектами напрямую, без ручного преобразования в строку.

// Настройка
val client = HttpClient {
    install(ContentNegotiation) {
        json(Gson {
            ignoreUnknownFields = true
            serializeNulls()
        })
    }
}

// Data class
data class Post(val userId: Int, val id: Int, val title: String, val body: String)

// Использование в запросе
suspend fun createPost(client: HttpClient): Post {
    val newPost = Post(userId = 1, id = 0, title = "My Post", body = "Content")
    val response: HttpResponse = client.post("https://jsonplaceholder.typicode.com/posts") {
        contentType(ContentType.Application.Json)
        setBody(newPost) // Автоматическая сериализация в JSON
    }
    return response.body() // Автоматическая десериализация из JSON в Post
}

Реальный практический пример: Консольный клиент для GitHub API

Давайте создадим полноценное консольное приложение, которое взаимодействует с GitHub API. Оно будет получать информацию о пользователе, список его репозиториев и выводить статистику. Этот пример объединяет все основные концепции: конфигурацию, GET/POST запросы, сериализацию, обработку ошибок и работу с корутинами.

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.gson.*
import kotlinx.coroutines.*
import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

// Data классы для десериализации ответов GitHub API
data class GitHubUser(
    val login: String,
    val id: Long,
    val name: String?,
    val public_repos: Int,
    val followers: Int,
    val following: Int,
    val avatar_url: String?,
    val html_url: String
)

data class GitHubRepo(
    val id: Long,
    val name: String,
    val full_name: String,
    val description: String?,
    val html_url: String,
    val stargazers_count: Int,
    val forks_count:                    

Рекомендаций