Введение
Kotlin — это современный, лаконичный и безопасный язык программирования, который набирает популярность в разработке серверных приложений. Благодаря своей совместимости с Java и мощным фреймворкам, таким как Ktor, вы можете создать полноценный REST API буквально за полчаса. В этой статье мы покажем, как написать REST API на Kotlin с использованием фреймворка Ktor, даже если вы новичок.
Мы создадим простое приложение для управления списком задач (Todo API) с поддержкой CRUD операций: создание, чтение, обновление и удаление. Весь процесс займет не более 30 минут, если следовать инструкции.
1. Подготовка проекта и зависимости
Для начала вам понадобится IntelliJ IDEA (Community Edition) и Gradle. Создадим новый проект Kotlin с поддержкой Ktor.
Шаг 1: Создание Gradle-проекта
Откройте IntelliJ IDEA и создайте новый проект: File → New → Project. Выберите Gradle и укажите язык Kotlin. Нажмите Finish.
Шаг 2: Добавление зависимостей Ktor
Откройте файл build.gradle.kts и добавьте следующие зависимости:
plugins {
kotlin("jvm") version "1.9.0"
application
}
repositories {
mavenCentral()
}
dependencies {
implementation("io.ktor:ktor-server-core:2.3.0")
implementation("io.ktor:ktor-server-netty:2.3.0")
implementation("io.ktor:ktor-server-content-negotiation:2.3.0")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.0")
implementation("ch.qos.logback:logback-classic:1.4.11")
}
application {
mainClass.set("ApplicationKt")
}
Нажмите кнопку Sync Now в правом верхнем углу, чтобы загрузить зависимости.
2. Создание модели данных и сервиса
Прежде чем писать маршруты, определим модель данных для нашей задачи и простой сервис для хранения данных в памяти.
Модель TodoItem
Создайте файл TodoItem.kt в папке src/main/kotlin:
import kotlinx.serialization.Serializable
@Serializable
data class TodoItem(
val id: Int,
val title: String,
val completed: Boolean = false
)
Сервис TodoService
Создайте файл TodoService.kt:
import java.util.concurrent.atomic.AtomicInteger
class TodoService {
private val items = mutableListOf<TodoItem>()
private val idCounter = AtomicInteger(1)
fun getAll(): List<TodoItem> = items.toList()
fun getById(id: Int): TodoItem? = items.find { it.id == id }
fun create(title: String): TodoItem {
val newItem = TodoItem(id = idCounter.getAndIncrement(), title = title)
items.add(newItem)
return newItem
}
fun update(id: Int, title: String?, completed: Boolean?): TodoItem? {
val index = items.indexOfFirst { it.id == id }
if (index == -1) return null
val current = items[index]
val updated = current.copy(
title = title ?: current.title,
completed = completed ?: current.completed
)
items[index] = updated
return updated
}
fun delete(id: Int): Boolean = items.removeAll { it.id == id }
}
3. Настройка сервера и маршрутов
Теперь создадим основной файл приложения Application.kt и пропишем маршруты REST API.
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.response.*
import io.ktor.server.request.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.plugins.contentnegotiation.*
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
configureSerialization()
configureRouting()
}.start(wait = true)
}
fun Application.configureSerialization() {
install(ContentNegotiation) {
json()
}
}
fun Application.configureRouting() {
val service = TodoService()
routing {
// GET /todos — получить все задачи
get("/todos") {
call.respond(service.getAll())
}
// GET /todos/{id} — получить задачу по ID
get("/todos/{id}") {
val id = call.parameters["id"]?.toIntOrNull()
if (id == null) {
call.respondText("Invalid ID", status = io.ktor.http.HttpStatusCode.BadRequest)
return@get
}
val item = service.getById(id)
if (item != null) {
call.respond(item)
} else {
call.respondText("Not Found", status = io.ktor.http.HttpStatusCode.NotFound)
}
}
// POST /todos — создать новую задачу
post("/todos") {
val request = call.receive<TodoItem>()
val newItem = service.create(request.title)
call.respond(newItem, status = io.ktor.http.HttpStatusCode.Created)
}
// PUT /todos/{id} — обновить задачу
put("/todos/{id}") {
val id = call.parameters["id"]?.toIntOrNull()
if (id == null) {
call.respondText("Invalid ID", status = io.ktor.http.HttpStatusCode.BadRequest)
return@put
}
val request = call.receive<TodoItem>()
val updated = service.update(id, request.title, request.completed)
if (updated != null) {
call.respond(updated)
} else {
call.respondText("Not Found", status = io.ktor.http.HttpStatusCode.NotFound)
}
}
// DELETE /todos/{id} — удалить задачу
delete("/todos/{id}") {
val id = call.parameters["id"]?.toIntOrNull()
if (id == null) {
call.respondText("Invalid ID", status = io.ktor.http.HttpStatusCode.BadRequest)
return@delete
}
if (service.delete(id)) {
call.respondText("Deleted", status = io.ktor.http.HttpStatusCode.OK)
} else {
call.respondText("Not Found", status = io.ktor.http.HttpStatusCode.NotFound)
}
}
}
}
4. Запуск и тестирование API
Запустите приложение, нажав зеленую стрелку рядом с функцией main(). Сервер стартует на порту 8080.
Тестирование с помощью curl
Откройте терминал и выполните следующие команды:
- Создать задачу:
curl -X POST http://localhost:8080/todos -H "Content-Type: application/json" -d