KtorJuniorTechnical

Как определять маршруты (routes) в Ktor и как работает вложенная маршрутизация?

Маршруты в Ktor определяются DSL-функциями get/post/put/delete внутри блока routing{}. Вложенность создаётся через route("/prefix") и позволяет группировать пути и применять общие плагины.

Базовая маршрутизация

Маршруты регистрируются через плагин Routing, который устанавливается автоматически при вызове routing{}. Внутри доступны функции-расширения для всех HTTP-методов:

import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun Application.configureRouting() {
    routing {
        get("/") {
            call.respondText("Welcome")
        }
        post("/echo") {
            val body = call.receiveText()
            call.respondText(body)
        }
        put("/items/{id}") {
            val id = call.parameters["id"]
            call.respondText("Updated $id")
        }
        delete("/items/{id}") {
            val id = call.parameters["id"] ?: return@delete call.respond(HttpStatusCode.BadRequest)
            call.respond(HttpStatusCode.NoContent)
        }
    }
}

Вложенная маршрутизация

Функция route() создаёт префикс-группу. Вложенность может быть произвольной глубины:

routing {
    route("/api") {
        route("/v1") {
            route("/users") {
                get {
                    // GET /api/v1/users
                    call.respond(listOf("Alice", "Bob"))
                }
                post {
                    // POST /api/v1/users
                    val user = call.receive<UserDto>()
                    call.respond(HttpStatusCode.Created, user)
                }
                get("/{id}") {
                    // GET /api/v1/users/{id}
                    val id = call.parameters["id"]
                    call.respondText("User: $id")
                }
            }
        }
    }
}

Параметры маршрута и query-параметры

get("/search") {
    // GET /search?q=kotlin&page=2
    val query = call.request.queryParameters["q"] ?: ""
    val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
    call.respondText("Query: $query, Page: $page")
}

get("/files/{path...}") {
    // catch-all: /files/a/b/c -> path = "a/b/c"
    val path = call.parameters.getAll("path")?.joinToString("/")
    call.respondText("File path: $path")
}

Разнесение маршрутов по файлам

Рекомендуемый подход — расширения на Route:

// routes/UserRoutes.kt
fun Route.userRoutes() {
    route("/users") {
        get { /* ... */ }
        post { /* ... */ }
    }
}

// Application.kt
fun Application.configureRouting() {
    routing {
        route("/api/v1") {
            userRoutes()
            productRoutes()
        }
    }
}

Подводные камни

  • Статические сегменты имеют приоритет над параметрическими: /users/me должен быть объявлен до /users/{id}, иначе «me» будет трактоваться как id.
  • Catch-all параметр {path...} должен быть последним сегментом — компилятор не запрещает иное, но маршрут просто никогда не совпадёт.
  • Отсутствие trailing slash обрабатывается отдельно: /users и /users/ — разные маршруты; используйте плагин IgnoreTrailingSlash чтобы обрабатывать оба.
  • call.parameters["id"] возвращает String?; не забывайте обрабатывать null, иначе получите NPE или 500.
  • Если routing не находит совпадение — Ktor возвращает 404, но без тела. Добавьте StatusPages для человекочитаемых ошибок.
  • Вложенный route{} не наследует аутентификацию родителя автоматически — нужно явно оборачивать в authenticate{}.

Common mistakes

  • Путать термин «ktor routing» с соседним механизмом Ktor.
  • Не называть границу lifecycle, transaction, thread или request для «ktor routing».
  • Игнорировать production-эффекты «ktor routing»: latency, SQL shape, memory, security или observability.

What the interviewer is testing

  • Попросить объяснить механизм «ktor routing» на минимальном примере.
  • Проверить, видит ли кандидат failure mode и диагностику для «ktor routing».
  • Уточнить, какие настройки или API меняют «ktor routing» в реальном сервисе.

Sources

Related topics