29 tháng Rio88 Game Bài Twin 9 năm 2023 Máy tính
Trong hai bài viết trước “Cách sử dụng Spring Boot và Kotlin để xây dựng dịch vụ RESTful API?” và “Cách sử dụng bộ công cụ HTTP Kotlin http4k để xây dựng dịch vụ RESTful API?”, chúng tôi đã giới thiệu cách sử dụng Spring Boot và http4k trong Kotlin để phát triển các API RESTful. Bài viết này sẽ tập trung vào việc làm thế nào để sử dụng khung nền web chính thức của Kotlin, Ktor, để phát triển API RESTful.
Bài viết sẽ lấy ví dụ về việc phát triển các API thêm, xóa, sửa, tra cứu cho đối tượng User nhằm học hỏi cách sử dụng Ktor. Dự án mẫu sử dụng Gradle để quản lý, cấu trúc dự án vẫn theo mô hình MVC ba lớp phổ biến nhất trong ngành; để làm nổi bật trọng tâm, bài viết không đề cập đến cơ sở dữ liệu và tầng DAO, thay vào đó sử dụng một danh sách List trong tầng Service làm nơi lưu trữ dữ liệu; để gần với tình huống thực tế hơn, ví dụ dự án này sử dụng Kodein để thực hiện tiêm phụ thuộc.
Toàn bộ bài viết được chia thành ba phần: giới thiệu cấu trúc dự án, phân tích sơ lược mã nguồn dự án, và kiểm thử API. Mong rằng sau khi đọc bài viết này, bạn sẽ có cái nhìn tổng quan về cách sử dụng Ktor để phát triển API.
Trước khi bắt đầu, hãy liệt kê các phiên bản phần mềm hoặc khung nền mà bài viết sử dụng:
Gradle: 8.3
Kotlin: 1.9.10
JDK: Amazon Corretto 17.0.8
Ktor: 2.3.4
1. Giới thiệu Cấu Trúc Dự Án
Dự án sử dụng Gradle để quản lý, cấu trúc dự án như sau:
ktor-restful-service-demo
|--- src/main/
| |--- resources/
| | |--- application.conf
| | \--- logback.xml
| \--- kotlin/
| \--- com.example.demo/
| |--- route/
| | \--- UserRoute.kt
| |--- service/
| | |--- UserService.kt
| |--- code/
| | \--- ErrorCodes.kt
| |--- model/
| | |--- ErrorResponse.kt
| | \--- User.kt
| |--- plugin/
| | |--- Routing.kt
| | \--- Serialization.kt
| |--- conf/
| | \--- KodeinConf.kt
| \--- DemoApplication.kt
...
|--- gradle/
|--- gradlew
\--- build.gradle.kts
Như có thể thấy, thư mục gốc chứa tệp cấu hình Gradle build.gradle.kts
, lệnh Gradle gradlew
và thư mục Gradle Wrapper gradle
; tiếp theo là thư mục cấu hình src/main/resources
và thư mục mã nguồn src/main/kotlin
.
Thư mục src/main/resources
có hai tệp: application.conf
và logback.xml
, lần lượt là tệp cấu hình cho máy chủ Ktor và tệp cấu hình ghi nhật ký Logback.
Sau đây là một số thư mục con bên dưới src/main/kotlin
:
- route Tương tự như tầng Controller của các khung nền khác, dùng để cấu hình định tuyến Ktor.
- service Tầng Service, tất cả logic kinh doanh chính được viết ở đây.
- code
Thư mục chứa lớp liệt kê
ErrorCodes.kt
, ví dụ dự án này sử dụng loại liệt kê này để lưu trữ mọi thông tin phản hồi lỗi. - model Thư mục chứa các lớp mô hình dữ liệu.
- plugin Thư mục chứa các plugin Ktor, dùng để cấu hình đường dẫn gốc và phương thức chuỗi hóa.
- conf
Thư mục chứa các lớp cấu hình, ví dụ cấu hình khung tiêm phụ thuộc Kodein
KodeinConf.kt
nằm tại đây.
Ngoài những gói này, src/main/kotlin
còn có một tệp DemoApplication.kt
, là điểm nhập của chương trình.
2. Phân Tích Mã Nguồn Dự Án
Phần trước đã giới thiệu cấu trúc thư mục và ý nghĩa của các gói trong ví dụ dự án, bây giờ chúng ta sẽ phân tích sơ lược tệp cấu hình Gradle và mã nguồn trong các gói.
2.1 Tệp cấu hình Gradle
Ví dụ dự án sử dụng Gradle để quản lý, nội dung tệp cấu hình build.gradle.kts
như sau:
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.10"
id("io.ktor.plugin") version "2.3.4"
}
application {
mainClass.set("com.example.demo.DemoApplicationKt")
}
repositories {
mavenCentral()
}
dependencies {
implementation("io.ktor:ktor-server-core")
implementation("io.ktor:ktor-server-netty")
implementation("io.ktor:ktor-server-content-negotiation")
implementation("io.ktor:ktor-serialization-jackson")
implementation("org.kodein.di:kodein-di:7.20.2")
implementation("ch.qos.logback:logback-classic:1.4.11")
}
Như có thể thấy, tệp này chỉ định phiên bản Kotlin là 1.9.10
, phiên bản Ktor là 2.3.4
; điểm nhập chương trình là DemoApplication.kt
; kho chứa là Maven Repository, các phụ thuộc bao gồm io.ktor:ktor-server-core
(thành phần cốt lõi của Ktor), io.ktor:ktor-server-netty
(động cơ máy chủ Netty được sử dụng), io.ktor:ktor-server-content-negotiation
(dùng để chuyển đổi chuỗi hóa và ngược chuỗi hóa giữa các đối tượng Kotlin và định dạng JSON), io.ktor:ktor-serialization-jackson
(thực hiện chuỗi hóa JSON bằng Jackson), org.kodein.di:kodein-di:7.20.2
(gói tiêm phụ thuộc Kodein), và ch.qos.logback:logback-classic:1.4.11
(gói in ra nhật ký Logback).
2.2 Mã nguồn trong gói plugin
Gói plugin có hai tệp: Routing.kt
và Serialization.kt
, lần lượt dùng để cấu hình đường dẫn gốc và phương thức chuỗi hóa.
Mã nguồn của Routing.kt
như sau:
// src/main/kotlin/com/example/demo/plugin/Routing.kt
package com.example.demo.plugin
import com.example.demo.route.userRouting
import io.ktor.server.application.*
import io.ktor.server.routing.*
fun Application.configureRouting() {
routing {
userRouting()
}
}
Như có thể thấy, đoạn mã trên chịu trách nhiệm cấu hình đường dẫn gốc của dự án, dự án này chỉ cấu hình một đường dẫn: userRouting()
, nằm trong gói route
, là quy tắc định tuyến cho User, chúng ta sẽ xem xét mã nguồn chi tiết sau.
Mã nguồn của Serialization.kt
như sau:
// src/main/kotlin/com/example/demo/plugin/Serialization.kt
package [m88vin - cổng game quốc tế](/post/muslim-funeral-reading/) com.example.demo.plugin
import io.ktor.serialization.jackson.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
fun Application.configureSerialization() {
install(ContentNegotiation) {
jackson()
}
}
Như có thể thấy, đoạn mã trên thiết lập Jackson làm phương thức chuỗi hóa và ngược chuỗi hóa nội dung.
2.3 Mã nguồn trong gói route
Route tương đương với Controller, chịu trách nhiệm nhận yêu cầu, gọi Service để xử lý, cuối cùng trả về phản hồi.
Gói route của ví dụ dự án này chỉ có một tệp UserRoute.kt
, mã nguồn của nó như sau:
// src/main/kotlin/com/example/demo/route/UserRoute.kt
package com.example.demo.route
import com.example.demo.code.ErrorCodes
import com.example.demo.conf.kodein
import com.example.demo.model.User
import com.example.demo.service.UserService
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.kodein.di.instance
fun Route.userRouting() {
val userService: UserService by kodein.instance()
route("/users") {
// liệt kê tất cả
get {
val users = userService.listAll()
call.respond(users)
}
// lấy User theo ID
get(Regex("/(?<id>\\d+)")) {
val id = call.parameters["id"]!!.toLong()
val user = userService.getById(id) ?: return@get call.respond(
ErrorCodes.USER_NOT_FOUND.status,
ErrorCodes.USER_NOT_FOUND.toErrorResponse()
)
call.respond(user)
}
// cập nhật
patch {
val user = call.receive<User>()
userService.getById(user.id) ?: return@patch call.respond(
ErrorCodes.USER_NOT_FOUND.status,
ErrorCodes.USER_NOT_FOUND.toErrorResponse()
)
userService.update(user)
call.respond(HttpStatusCode.NoContent)
}
// lưu
post {
val user = call.receive<User>()
userService.getById(user.id)?.let {
return@post call.respond(
ErrorCodes.USER_ALREADY_EXISTS.status,
ErrorCodes.USER_ALREADY_EXISTS.toErrorResponse()
)
}
userService.save(user)
call.respond(HttpStatusCode.Created)
}
// xóa theo ID
delete(Regex("/(?<id>\\d+)")) {
val id = call.parameters["id"]!!.toLong()
userService.getById(id) ?: return@delete call.respond(
ErrorCodes.USER_NOT_FOUND.status,
ErrorCodes.USER_NOT_FOUND.toErrorResponse()
)
userService.deleteById(id)
call.respond(HttpStatusCode.NoContent)
}
}
}
Như có thể thấy, đoạn mã trên userRouting
là hàm mở rộng của Route
, sử dụng cách tiêm Kodein để lấy được thể hiện của UserService
; có năm API, lần lượt là: lấy tất cả User, lấy một User duy nhất, cập nhật User, tạo mới User, và xóa User; bên trong đều gọi UserService
để thực hiện, đối với thông tin phản hồi lỗi, đều sử dụng loại liệt kê thống nhất ErrorCodes.kt
.
Tiếp tục phần còn lại nếu cần thiết…