基础入门#
更改默认端口#
代码配置#
Application.kt
1
2
3
4
5
6
7
8
9
10
11
12
| fun main() {
embeddedServer(
Netty,
port = 8080, //更改端口
host = "0.0.0.0",
module = Application::module
).start(wait = true)
}
fun Application.module() {
configureRouting()
}
|
yaml配置#
Application.kt
1
2
3
4
5
6
7
| fun main(args: Array<String>): Unit =
io.ktor.server.netty.EngineMain.main(args)
@Suppress("unused")
fun Application.module() {
configureRouting()
}
|
application.yaml
1
2
3
4
5
6
| ktor:
application:
modules:
- com.example.ApplicationKt.module
deployment:
port: 8080
|
添加新端点#
plugins/Routing.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
| fun Application.configureRouting() {
routing {
get("/") {
call.respondText("Hello World!")
}
get("/test1") {
val text = "<h1>Hello From Ktor</h1>"
val type = ContentType.parse("text/html")
call.respondText(text, type)
}
}
}
|
访问http://localhost:8080/test1即可看到相应
配置静态资源#
plugins/Routing.kt
1
2
3
4
5
6
7
8
9
10
| fun Application.configureRouting() {
routing {
//在这里配置资源
staticResources("/content", "mycontent")
get("/") {
call.respondText("Hello World!")
}
}
}
|
解释:验证 staticResources() 告诉Ktor,我们希望我们的应用程序能够提供标准的网站内容,例如HTML和JavaScript文件。虽然这些内容可以在浏览器中执行,但从服务器的角度来看,它被认为是静态的。
URL /content 指定用于获取此内容的路径。
路径 mycontent 是静态内容所在文件夹的名称。Ktor将在 resources 目录中查找此文件夹。
集成测试#
src/test/kotlin/ApplicationTest.kt
1
2
3
4
5
6
7
8
9
10
11
12
| @Test
fun testNewEndpoint() = testApplication {
application {
module()
}
val response = client.get("/test1")
assertEquals(HttpStatusCode.OK, response.status)
assertEquals("html", response.contentType()?.contentSubtype)
assertContains(response.bodyAsText(), "Hello From Ktor")
}
|
自定义异常处理#
依赖:implementation(“io.ktor:ktor-server-status-pages:$ktor_version”)
plugins/Routing.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| fun Application.configureRouting() {
// 指定错误处理
install(StatusPages) {
exception<IllegalStateException> { call, cause ->
call.respondText("App in illegal state as ${cause.message}")
}
}
routing {
get("/") {
call.respondText("Hello World!")
}
get("/error-test") {
throw IllegalStateException("Too Busy")
// 触发异常处理
}
}
}
|
创建HTTP API#
基本依赖#
1
2
3
4
5
6
7
8
9
10
11
12
| plugins {
kotlin("plugin.serialization") version "1.9.10"
}
dependencies {
implementation("io.ktor:ktor-server-core-jvm")
implementation("io.ktor:ktor-server-content-negotiation-jvm")
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm")
implementation("io.ktor:ktor-server-netty-jvm")
implementation("ch.qos.logback:logback-classic:$logback_version")
testImplementation("io.ktor:ktor-server-test-host:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
}
|
添加并定义程序模块#
Application.kt
1
2
3
4
5
6
| fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
fun Application.module() {
configureRouting()//错误处理路由
configureSerialization()//添加新模块
}
|
plugins/Serialization.kt
1
2
3
4
5
| fun Application.configureSerialization() {
install(ContentNegotiation) {
json()
}
}
|
创建客户模型#
Customer.kt
1
2
3
4
5
6
| package com.example.models
import kotlinx.serialization.Serializable
@Serializable
data class Customer(val id: String, val firstName: String, val lastName: String, val email: String)
|
@Serializable 注解。结合其 Ktor 集成,这将使我们能够自动生成 API 响应所需的 JSON 表示
创建内存存储#
1
| val customerStorage = mutableListOf<Customer>()
|
创建路由#
customerRouting.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| fun Route.customerRouting() {
route("/customer") {
get {
}
get("{id?}") {
}
post {
}
delete("{id?}") {
}
}
}
|
构建响应#
json序列化#
customerRouting.kt
1
2
3
4
5
6
7
8
9
10
11
| fun Route.customerRouting() {
route("/customer") {
get {
if (customerStorage.isNotEmpty()) {
call.respond(customerStorage)//自动配置json序列化
} else {
call.respondText("No customers found", status = HttpStatusCode.OK)
}
}
}
}
|
路径传参#
customerRouting.kt
1
2
3
4
5
6
7
8
9
10
11
12
| get("{id?}") {
val id = call.parameters["id"] ?: return@get call.respondText(
"Missing id",
status = HttpStatusCode.BadRequest
)
val customer =
customerStorage.find { it.id == id } ?: return@get call.respondText(
"No customer with id $id",
status = HttpStatusCode.NotFound
)
call.respond(customer)
}
|
请求解析#
customerRouting.kt
1
2
3
4
5
| post {
val customer = call.receive<Customer>()
customerStorage.add(customer)
call.respondText("Customer stored correctly", status = HttpStatusCode.Created)
}
|
call.receive 与配置的内容协商插件集成。使用 generic 参数调用它会 Customer 自动将 JSON 请求正文反序列化为 Kotlin Customer 对象
注册路由#
plugins/Routing.kt
1
2
3
4
5
6
7
8
9
10
11
| package com.example.plugins
import com.example.routes.*
import io.ktor.server.application.*
import io.ktor.server.routing.*
fun Application.configureRouting() {
routing {
customerRouting()
}
}
|
规范化路由定义#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| fun Route.listOrdersRoute() {
get("/order") {
if (orderStorage.isNotEmpty()) {
call.respond(orderStorage)
}
}
}
fun Route.getOrderRoute() {
get("/order/{id?}") {
val id = call.parameters["id"] ?: return@get call.respondText("Bad Request", status = HttpStatusCode.BadRequest)
val order = orderStorage.find { it.number == id } ?: return@get call.respondText(
"Not Found",
status = HttpStatusCode.NotFound
)
call.respond(order)
}
}
fun Route.totalizeOrderRoute() {
get("/order/{id?}/total") {
val id = call.parameters["id"] ?: return@get call.respondText("Bad Request", status = HttpStatusCode.BadRequest)
val order = orderStorage.find { it.number == id } ?: return@get call.respondText(
"Not Found",
status = HttpStatusCode.NotFound
)
val total = order.contents.sumOf { it.price * it.amount }
call.respond(total)
}
}
|
跨域配置#
Application.module()
1
2
3
| install(CORS) {
anyHost()
}
|