HTTP клиент на Kotlin: делаем запросы правильно с ktor и OkHttp

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

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

Начать курс

HTTP клиент на Kotlin: делаем запросы правильно

Современная разработка под Android и бэкенд-сервисы на Kotlin редко обходится без взаимодействия с удаленными API. Создание надежного HTTP клиента — базовая задача, от правильного решения которой зависит стабильность всего приложения. В этой статье мы разберем, как делать HTTP запросы на Kotlin, используя две самые популярные библиотеки: Ktor Client и OkHttp. Вы узнаете, как отправлять GET и POST запросы, обрабатывать JSON, управлять ошибками и писать чистый, поддерживаемый код.

Почему Ktor Client — выбор современного Kotlin-разработчика

Ktor Client — это асинхронный HTTP клиент, созданный специально для Kotlin командой JetBrains. Он полностью поддерживает корутины (coroutines), что делает код лаконичным и безопасным. В отличие от OkHttp, Ktor изначально спроектирован с учетом мультиплатформенности: один и тот же код работает на Android, iOS, JVM, JS и Native.

Установка зависимостей

Для работы с Ktor Client добавьте следующие зависимости в файл build.gradle.kts:

dependencies {
    // Базовый клиент
    implementation("io.ktor:ktor-client-core:2.3.7")
    // Движок для JVM (OkHttp или CIO)
    implementation("io.ktor:ktor-client-okhttp:2.3.7")
    // Для сериализации JSON
    implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
    implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
    // Kotlinx Serialization
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}

Простой GET запрос

Создадим HTTP клиент и выполним GET запрос к тестовому API (JSONPlaceholder):

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

suspend fun fetchUser(userId: Int): String {
    val client = HttpClient {
        // Базовая конфигурация
        engine {
            // Настройки OkHttp движка
        }
    }
    val response: HttpResponse = client.get("https://jsonplaceholder.typicode.com/users/$userId")
    return response.bodyAsText()
}

fun main() = runBlocking {
    val userData = fetchUser(1)
    println(userData)
}

Обратите внимание: fetchUser — suspend функция. Это позволяет вызывать её из корутины без блокировки потока.

POST запрос с JSON телом

Для отправки данных в формате JSON подключим плагин ContentNegotiation и определим data-класс:

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Serializable
data class Post(val userId: Int, val id: Int? = null, val title: String, val body: String)

suspend fun createPost(post: Post): Post {
    val client = HttpClient {
        install(ContentNegotiation) {
            json(Json {
                ignoreUnknownKeys = true
                prettyPrint = true
            })
        }
    }
    val response: Post = client.post("https://jsonplaceholder.typicode.com/posts") {
        contentType(ContentType.Application.Json)
        setBody(post)
    }.body()
    client.close()
    return response
}

fun main() = runBlocking {
    val newPost = Post(userId = 1, title = "Ktor Client", body = "Работает отлично!")
    val createdPost = createPost(newPost)
    println(createdPost)
}

Ключевой момент: плагин ContentNegotiation автоматически сериализует объект в JSON и десериализует ответ обратно в data-класс.

OkHttp — проверенный стандарт для Android

OkHttp остается де-факто стандартом для сетевых запросов в Android-разработке. Он быстрый, надежный и поддерживает перехватчики (interceptors), кэширование и WebSocket. Рассмотрим, как использовать OkHttp в Kotlin-проекте.

Базовая настройка и GET запрос

import okhttp3.OkHttpClient
import okhttp3.Request
import java.util.concurrent.TimeUnit

val client = OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .build()

fun fetchUsers(): String {
    val request = Request.Builder()
        .url("https://jsonplaceholder.typicode.com/users")
        .get()
        .build()

    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) throw RuntimeException("Ошибка: ${response.code}")
        return response.body?.string() ?: "Пустой ответ"
    }
}

fun main() {
    val users = fetchUsers()
    println(users)
}

Обратите внимание на .use — это гарантирует закрытие ресурсов (Response body) даже при возникновении исключения.

POST запрос с JSON на OkHttp

Для отправки JSON потребуется создать тело запроса вручную или использовать библиотеку сериализации (например, Gson или kotlinx.serialization):

import com.google.gson.Gson
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

data class User(val name: String, val email: String)

fun createUser(user: User): String {
    val json = Gson().toJson(user)
    val mediaType = "application/json; charset=utf-8".toMediaType()
    val requestBody = json.toRequestBody(mediaType)

    val request = Request.Builder()
        .url("https://jsonplaceholder.typicode.com/users")
        .post(requestBody)
        .build()

    client.newCall(request).execute().use { response ->
        return response.body?.string() ?: "Ошибка"
    }
}

fun main() {
    val newUser = User(name = "Иван Петров", email = "ivan@example.com")
    val result = createUser(newUser)
    println(result)
}

OkHttp не требует корутин для базовых запросов, но для асинхронной работы используйте enqueue с callback или оборачивайте вызовы в корутины с помощью suspendCancellableCoroutine.

Обработка ошибок и лучшие практики

Независимо от выбранной библиотеки, важно правильно обрабатывать ошибки и структурировать код. Вот несколько рекомендаций:

  • Используйте sealed class для состояний ответа. Создайте обертку, которая возвращает успешный результат или ошибку, вместо выбрасывания исключений.
  • Настройте таймауты. Всегда устанавливайте

Блоги

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