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

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

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

Начать курс

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

Современная разработка на Kotlin немыслима без сетевого взаимодействия. Будь то Android-приложение, серверное решение на Ktor или десктопное приложение — HTTP-запросы остаются основным способом обмена данными. В этой статье мы разберем, как правильно организовать HTTP-клиент на Kotlin, рассмотрим популярные библиотеки и лучшие практики.

Почему Kotlin идеален для HTTP-запросов?

Kotlin предлагает современный синтаксис, нулевую безопасность и полную совместимость с Java-экосистемой. Благодаря корутинам и потоковым API, работа с сетью становится чистой и асинхронной. Основные преимущества:

  • Корутины — легковесные потоки для асинхронных операций без блокировок
  • Type-safe builders — удобное конструирование HTTP-запросов
  • Sealed classes — элегантная обработка состояний ответа
  • Extension functions — расширение функциональности библиотек

Выбор HTTP-клиента для Kotlin

На сегодняшний день три библиотеки доминируют в экосистеме Kotlin: OkHttp, Ktor Client и Retrofit. Рассмотрим каждую с примерами кода.

1. OkHttp — проверенный временем выбор

OkHttp от Square — наиболее популярный HTTP-клиент для JVM. Он эффективен, поддерживает HTTP/2, автоматическое сжатие и кэширование.

// build.gradle.kts
implementation("com.squareup.okhttp3:okhttp:4.12.0")

// Пример GET-запроса с OkHttp
import okhttp3.OkHttpClient
import okhttp3.Request
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class OkHttpExample {
    private val client = OkHttpClient.Builder()
        .connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
        .readTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
        .build()

    suspend fun fetchData(url: String): String = withContext(Dispatchers.IO) {
        val request = Request.Builder()
            .url(url)
            .header("Accept", "application/json")
            .build()

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

2. Ktor Client — нативный выбор для Kotlin

Ktor Client — часть экосистемы JetBrains, полностью написан на Kotlin с поддержкой мультиплатформенности. Идеален для Ktor-серверов и Android/iOS проектов.

// build.gradle.kts
implementation("io.ktor:ktor-client-core:2.3.7")
implementation("io.ktor:ktor-client-cio:2.3.7")
implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable

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

class KtorExample {
    private val client = HttpClient(CIO) {
        install(ContentNegotiation) {
            json(Json {
                ignoreUnknownKeys = true
                isLenient = true
            })
        }
        engine {
            requestTimeout = 30_000
        }
    }

    suspend fun getPost(id: Int): Post {
        val response: HttpResponse = client.get("https://jsonplaceholder.typicode.com/posts/$id")
        return response.body()
    }

    suspend fun createPost(post: Post): Post {
        val response: HttpResponse = client.post("https://jsonplaceholder.typicode.com/posts") {
            setBody(post)
            contentType(io.ktor.http.ContentType.Application.Json)
        }
        return response.body()
    }
}

3. Retrofit — стандарт для Android-разработки

Retrofit — высокоуровневая обертка над OkHttp, используемая в большинстве Android-проектов. Позволяет декларативно описывать API.

// build.gradle.kts
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.*
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor

interface ApiService {
    @GET("posts/{id}")
    suspend fun getPost(@Path("id") id: Int): Post

    @POST("posts")
    suspend fun createPost(@Body post: Post): Post

    @GET("posts")
    suspend fun getPostsByUser(@Query("userId") userId: Int): List<Post>
}

object RetrofitClient {
    private val logging = HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    }

    private val client = OkHttpClient.Builder()
        .addInterceptor(logging)
        .connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
        .build()

    val apiService: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl("https://jsonplaceholder.typicode.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

Продвинутые техники работы с HTTP-клиентом

Обработка ошибок и повторные попытки

Надежное приложение должно корректно обрабатывать сетевые ошибки. Используйте sealed class для моделирования состояний ответа.

sealed class NetworkResult<out T> {
    data class Success<out T>(val data: T) : NetworkResult<T>()
    data class Error(val message: String, val code: Int? = null) : NetworkResult<Nothing>()
    object Loading : NetworkResult<Nothing>()
}

suspend fun <T> safeApiCall(call: suspend () -> T): NetworkResult<T> {
    return try {
        NetworkResult.Success(call())
    } catch (e: IOException) {
        NetworkResult.Error("Нет соединения с интернетом: ${e.message}")
    } catch (e: HttpException) {
        NetworkResult.Error("Ошибка сервера: ${e.code()}", e.code())
    } catch (e: Exception) {
        NetworkResult.Error("Неизвестная ошибка: ${e.message}")
    }
}

// Использование
fun main() = runBlocking {
    val result = safeApiCall {
        RetrofitClient.apiService.getPost(1)
    }
    when (result) {
        is                

Блоги

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