HTTP клиент на Kotlin: делаем запросы правильно от А до Я
Современная разработка на Kotlin немыслима без взаимодействия с удалёнными серверами. Будь то мобильное приложение на Android или серверное решение на Ktor — HTTP-запросы являются основой обмена данными. В этой статье мы разберём, как создавать HTTP-клиенты на Kotlin, используя лучшие практики, популярные библиотеки и современный подход с корутинами. Вы узнаете, как делать GET и POST запросы, обрабатывать ошибки, работать с JSON и строить надёжную архитектуру сетевого слоя.
Почему Kotlin идеален для HTTP-клиентов?
Kotlin предлагает лаконичный синтаксис, нулевую безопасность (null safety) и полную совместимость с Java-экосистемой. Благодаря корутинам (coroutines) асинхронные запросы становятся интуитивно понятными, без громоздких колбэков. Вы можете писать последовательный код, который выполняется асинхронно — это значительно упрощает чтение и отладку. Кроме того, Kotlin активно используется в Android-разработке, где сетевые запросы — повседневная задача.
Основные библиотеки для HTTP в Kotlin
На сегодняшний день наиболее популярны три подхода:
- Ktor Client — мультиплатформенный клиент от JetBrains. Поддерживает JVM, Android, iOS, JS и Native. Идеален для проектов, где нужна единая кодовая база.
- OkHttp + Retrofit — классический стек для Android/JVM. OkHttp отвечает за низкоуровневую работу с сетью, а Retrofit генерирует типизированные интерфейсы.
- Fuel — лёгкая библиотека с функциональным подходом. Подходит для небольших проектов.
В этой статье мы сосредоточимся на Ktor и OkHttp, так как они покрывают 90% сценариев.
Настройка проекта и первые запросы
Для начала добавим зависимости. Если вы используете Gradle (Kotlin DSL), подключите Ktor Client:
dependencies {
implementation("io.ktor:ktor-client-core:2.3.7")
implementation("io.ktor:ktor-client-cio:2.3.7") // движок CIO
implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
}
Для OkHttp зависимости будут такими:
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation("com.google.code.gson:gson:2.10.1")
}
GET-запрос с Ktor Client
Создадим простой клиент и выполним GET-запрос к публичному API:
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
suspend fun fetchData(): String {
val client = HttpClient()
val response: HttpResponse = client.get("https://jsonplaceholder.typicode.com/posts/1")
val body = response.bodyAsText()
client.close()
return body
}
// Использование в корутине
fun main() = runBlocking {
println(fetchData())
}
Обратите внимание: suspend — ключевое слово Kotlin для приостанавливаемых функций. Это позволяет выполнять запрос без блокировки потока.
GET-запрос с OkHttp
Аналогичный пример на OkHttp выглядит так:
import okhttp3.OkHttpClient
import okhttp3.Request
fun fetchDataSync(): String {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/posts/1")
.build()
client.newCall(request).execute().use { response ->
return response.body?.string() ?: "Ошибка"
}
}
// Вызов в обычном потоке (блокирующий)
fun main() {
println(fetchDataSync())
}
OkHttp по умолчанию синхронный, но его легко адаптировать под корутины с помощью suspendCancellableCoroutine.
POST-запросы и работа с JSON
Отправка данных — вторая важнейшая операция. Рассмотрим, как отправить JSON-объект на сервер.
POST с Ktor и сериализацией
Ktor отлично интегрируется с kotlinx.serialization. Сначала определим data-класс:
@Serializable
data class Post(val userId: Int, val id: Int? = null, val title: String, val body: String)
Теперь выполним POST:
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.*
suspend fun createPost(): Post {
val client = HttpClient {
install(ContentNegotiation) {
json()
}
}
val newPost = Post(userId = 1, title = "Kotlin", body = "HTTP client")
val response = client.post("https://jsonplaceholder.typicode.com/posts") {
contentType(ContentType.Application.Json)
setBody(newPost)
}
return response.body()
}
POST с OkHttp и Gson
Для OkHttp потребуется вручную сериализовать объект:
import com.google.gson.Gson
import okhttp3.*
fun createPostSync(): String {
val client = OkHttpClient()
val gson = Gson()
val post = Post(userId = 1, title = "Kotlin", body = "HTTP client")
val json = gson.toJson(post)
val requestBody = json.toRequestBody("application/json; charset=utf-8".toMediaType())
val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/posts")
.post(requestBody)
.build()
client.newCall(request).execute().use { response ->
return response.body?.string() ?: "Ошибка"
}
}
Обработка ошибок и таймауты
Сеть нестабильна, поэтому обработка ошибок обязательна. Рассмотрим типовые сценарии.
Ktor: обработка исключений
Ktor выбрасывает исключения при сетевых ошибках. Используйте try-catch:
suspend fun safeFetch(): String {
return try {
val client = HttpClient()
val response = client.get("https://example.com/api")
response.bodyAsText()
} catch (e: Exception) {
"Ошибка: ${e.message}"
} finally {
client.close()
}
}
Также можно настроить таймауты:
val client = HttpClient {