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

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

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

Начать курс

Что такое Ktor и зачем он нужен?

Ktor — это асинхронный веб-фреймворк, написанный на языке Kotlin компанией JetBrains (создатели IntelliJ IDEA и самого Kotlin). Он предназначен для создания серверных приложений, HTTP-клиентов и микросервисов с использованием мощностей корутин Kotlin. Ktor полностью построен на корутинах, что делает его неблокирующим, легковесным и невероятно производительным.

Зачем использовать Ktor? Во-первых, это нативный Kotlin-фреймворк, который идеально интегрируется с экосистемой языка. Во-вторых, Ktor модульный: вы подключаете только те компоненты, которые вам нужны (маршрутизация, сериализация, аутентификация, WebSockets и т.д.). В-третьих, он поддерживает мультиплатформенность: один и тот же код клиента может работать на JVM, Android, iOS и JavaScript. Ktor идеально подходит для создания REST API, микросервисов, а также для написания HTTP-клиентов, которые могут работать на любых платформах.

Установка Ktor

Ktor — это не одна библиотека, а набор Gradle-зависимостей. Установка происходит через систему сборки Gradle (или Maven). Для начала создайте новый проект Kotlin и добавьте следующие зависимости в файл build.gradle.kts:

// build.gradle.kts
val ktorVersion = "2.3.12" // Актуальная версия на момент написания

plugins {
    kotlin("jvm") version "1.9.22"
    application
}

dependencies {
    // Сервер Ktor
    implementation("io.ktor:ktor-server-core:$ktorVersion")
    implementation("io.ktor:ktor-server-netty:$ktorVersion") // Движок Netty
    implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion") // Для JSON
    implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") // Сериализация JSON

    // Клиент Ktor (если нужен)
    implementation("io.ktor:ktor-client-core:$ktorVersion")
    implementation("io.ktor:ktor-client-cio:$ktorVersion") // Движок CIO
    implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")

    // Логирование
    implementation("ch.qos.logback:logback-classic:1.4.14")
}

application {
    mainClass.set("ApplicationKt") // Точка входа
}

После этого выполните команду ./gradlew build для загрузки зависимостей. Если вы используете Maven, аналогичные зависимости можно найти на официальном сайте Ktor. Для быстрого старта также можно использовать генератор проектов на start.ktor.io.

Быстрый старт с примером кода

Давайте создадим простой HTTP-сервер, который отвечает "Hello, World!" на GET-запрос к корневому пути.

import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/") {
                call.respondText("Hello, World!")
            }
        }
    }.start(wait = true)
}

Запустите этот код. Теперь откройте браузер и перейдите по адресу http://localhost:8080. Вы увидите текст "Hello, World!". Поздравляю, ваш первый Ktor-сервер запущен!

Основные методы и функции Ktor

Ниже представлены 10 ключевых методов/функций Ktor, которые вы будете использовать чаще всего.

1. embeddedServer

Что делает: Создает и запускает встроенный HTTP-сервер. Это основная точка входа для любого Ktor-приложения.

Параметры:

  • engine — движок сервера (Netty, CIO, Jetty, Tomcat).
  • port — порт для прослушивания (по умолчанию 80).
  • host — хост (по умолчанию 0.0.0.0).
  • module — лямбда-функция, в которой настраивается приложение (маршруты, плагины).

Пример:

embeddedServer(Netty, port = 9000, host = "127.0.0.1") {
    // Настройка приложения
}.start(wait = true)

2. routing

Что делает: Блок, внутри которого определяются маршруты (endpoints) вашего API. Это функция-расширение для Application.

Параметры: Не принимает параметров, но внутри используются методы get, post, put, delete и т.д.

Пример:

routing {
    get("/api/users") { /* обработка */ }
    post("/api/users") { /* обработка */ }
}

3. get / post / put / delete

Что делает: Определяют обработчик для соответствующего HTTP-метода и пути.

Параметры:

  • path — строка пути (например, "/user/{id}").
  • body — лямбда-функция (PipelineContext<Unit, ApplicationCall>), где доступен объект call.

Пример:

get("/hello") {
    call.respondText("Hello")
}

post("/data") {
    val body = call.receive<String>()
    call.respondText("Received: $body")
}

4. call.respond / call.respondText

Что делает: Отправляет ответ клиенту. respondText отправляет простой текст, respond может отправлять любые объекты (с использованием Content Negotiation).

Параметры:

  • respondText: text (String), status (HttpStatusCode, опционально).
  • respond: body (любой объект), status (опционально).

Пример:

get("/json") {
    val user = User("Alice", 30)
    call.respond(user) // Требует плагин ContentNegotiation
}

get("/text") {
    call.respondText("Plain text", status = HttpStatusCode.OK)
}

5. call.receive

Что делает: Получает тело запроса и десериализует его в указанный тип. Работает в паре с плагином Content Negotiation.

Параметры:

  • type — тип, в который нужно преобразовать тело запроса (указывается в угловых скобках).

Пример:

data class User(val name: String, val age: Int)

post("/user") {
    val user = call.receive<User>()
    println("Received user: ${user.name}")
    call.respondText("User created")
}

6. install (Плагины)

Что делает: Устанавливает плагин (модуль) в приложение. Плагины добавляют функциональность: сериализация, аутентификация, сжатие, CORS и т.д.

Параметры:

  • plugin — объект плагина (например, ContentNegotiation).
  • configure — лямбда для настройки плагина.

Пример:

install(ContentNegotiation) {
    json(Json {
        prettyPrint = true
        isLenient = true
    })
}

install(CORS) {
    allowHost("example.com")
}

7. call.parameters

Что делает: Предоставляет доступ к параметрам пути (path parameters) и query-параметрам из URL.

Параметры: Не принимает, это свойство.

Пример:

get("/user/{id}") {
    val userId = call.parameters["id"] ?: return@get call.respondText("Missing id", status = HttpStatusCode.BadRequest)
    call.respondText("User ID: $userId")
}

get("/search") {
    val query = call.request.queryParameters["q"] ?: "default"
    call.respondText("Search query: $query")
}

8. call.request

Что делает: Предоставляет доступ к объекту запроса: заголовки, метод, URI, cookies и т.д.

Параметры: Свойство, не принимает параметров.

Пример:

get("/info") {
    val method = call.request.httpMethod.value
    val uri = call.request.uri
    val userAgent = call.request.headers["User-Agent"]
    call.respondText("Method: $method, URI: $uri, UA: $userAgent")
}

9. HttpClient (Клиентская часть)

Что делает: Создает HTTP-клиент для выполнения исходящих запросов. Ktor предоставляет мощный асинхронный клиент.

Параметры:

  • engine — движок (CIO, Apache, OkHttp, Android).
  • block — лямбда для настройки клиента.

Пример:

suspend fun fetchData(): String {
    val client = HttpClient(CIO) {
        install(ContentNegotiation) {
            json()
        }
    }
    val response: String = client.get("https://api.example.com/data").body()
    client.close()
    return response
}

10. client.get / client.post (Методы клиента)

Что делает: Выполняет HTTP-запрос указанного метода. Возвращает HttpResponse, из которого можно извлечь тело.

Параметры:

  • url — строка URL.
  • block — лямбда для настройки запроса (заголовки, тело).

Пример:

suspend fun postJson() {
    val client = HttpClient()
    val response = client.post("https://httpbin.org/post") {
        contentType(ContentType.Application.Json)
        setBody(mapOf("key" to "value"))
    }
    println(response.bodyAsText())
    client.close()
}

Реальный практический пример использования

Давайте создадим полноценный REST API для управления списком задач (To-Do List) с использованием Ktor и хранением данных в памяти.

import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.Serializable
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap

@Serializable
data class Task(val id: String = UUID.randomUUID().toString(), val title: String, val completed: Boolean = false)

@Serializable
data class CreateTaskRequest(val title: String)

// Хранилище в памяти (потокобезопасное)
val taskStorage = ConcurrentHashMap<String, Task>()

fun main() {
    embeddedServer(Netty, port = 8080) {
        // Устанавливаем плагин для работы с JSON
        install(ContentNegotiation) {
            json()
        }

        routing {
            // Получить все задачи
            get("/tasks") {
                call.respond(taskStorage.values.toList())
            }

            // Получить задачу по ID
            get("/tasks/{id}") {
                val id = call.parameters                    

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