Ktor 服务器中的内容协商与序列化
所需依赖项: io.ktor:ktor-server-content-negotiation
代码示例: json-kotlinx
ContentNegotiation 插件有两个主要目的:
- 在客户端和服务器之间协商媒体类型。为此,它使用
Accept
和Content-Type
头。 - 以特定格式序列化/反序列化内容。Ktor 开箱即用地支持以下格式:JSON、XML、CBOR 和 ProtoBuf。
在客户端,Ktor 提供了 ContentNegotiation 插件用于序列化/反序列化内容。
添加依赖项
ContentNegotiation
要使用 ContentNegotiation
,您需要在构建脚本中包含 ktor-server-content-negotiation
artifact:
请注意,特定格式的序列化器需要额外的 artifact。例如,kotlinx.serialization 需要 ktor-serialization-kotlinx-json
依赖项来处理 JSON。
序列化
在使用 kotlinx.serialization 转换器之前,您需要按照 设置 部分的说明添加 Kotlin 序列化插件。
JSON
要序列化/反序列化 JSON 数据,您可以选择以下库之一:kotlinx.serialization、Gson 或 Jackson。
在构建脚本中添加 ktor-serialization-kotlinx-json
artifact:
在构建脚本中添加 ktor-serialization-gson
artifact:
在构建脚本中添加 ktor-serialization-jackson
artifact:
XML
要序列化/反序列化 XML,请在构建脚本中添加 ktor-serialization-kotlinx-xml
:
请注意,XML 序列化 不支持
jsNode
目标平台。
CBOR
要序列化/反序列化 CBOR,请在构建脚本中添加 ktor-serialization-kotlinx-cbor
:
ProtoBuf
要序列化/反序列化 ProtoBuf,请在构建脚本中添加 ktor-serialization-kotlinx-protobuf
:
安装 ContentNegotiation
要将 ContentNegotiation
插件安装到应用程序中, 请将其传递给指定
install
函数。 以下代码片段展示了如何安装 ContentNegotiation
... - ... 在
embeddedServer
函数调用内部。 - ... 在显式定义的
module
内部,它是Application
类的扩展函数。
配置序列化器
Ktor 开箱即用地支持以下格式:JSON、XML、CBOR。您还可以实现自己的自定义序列化器。
JSON 序列化器
要在应用程序中注册 JSON 序列化器,请调用 json
方法:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
install(ContentNegotiation) {
json()
}
json
方法还允许您调整 JsonBuilder 提供的序列化设置,例如:
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
})
要在应用程序中注册 Gson 序列化器,请调用 gson
方法:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.gson.*
install(ContentNegotiation) {
gson()
}
gson
方法还允许您调整 GsonBuilder 提供的序列化设置,例如:
install(ContentNegotiation) {
gson {
registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
setDateFormat(DateFormat.LONG, DateFormat.SHORT)
setPrettyPrinting()
}
要在应用程序中注册 Jackson 序列化器,请调用 jackson
方法:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.jackson.*
install(ContentNegotiation) {
jackson()
}
jackson
方法还允许您调整 ObjectMapper 提供的序列化设置,例如:
install(ContentNegotiation) {
jackson {
configure(SerializationFeature.INDENT_OUTPUT, true)
setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "
"))
})
registerModule(JavaTimeModule()) // support java.time.* types
}
}
XML 序列化器
要在应用程序中注册 XML 序列化器,请调用 xml
方法:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
install(ContentNegotiation) {
xml()
}
xml
方法还允许您访问 XML 序列化设置,例如:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
import nl.adaptivity.xmlutil.*
import nl.adaptivity.xmlutil.serialization.*
install(ContentNegotiation) {
xml(format = XML {
xmlDeclMode = XmlDeclMode.Charset
})
}
CBOR 序列化器
要在应用程序中注册 CBOR 序列化器,请调用 cbor
方法:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*
install(ContentNegotiation) {
cbor()
}
cbor
方法还允许您访问 CborBuilder 提供的 CBOR 序列化设置,例如:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*
import kotlinx.serialization.cbor.*
install(ContentNegotiation) {
cbor(Cbor {
ignoreUnknownKeys = true
})
}
ProtoBuf 序列化器
要在应用程序中注册 ProtoBuf 序列化器,请调用 protobuf
方法:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*
install(ContentNegotiation) {
protobuf()
}
protobuf
方法还允许您访问 ProtoBufBuilder 提供的 ProtoBuf 序列化设置,例如:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*
import kotlinx.serialization.protobuf.*
install(ContentNegotiation) {
protobuf(ProtoBuf {
encodeDefaults = true
})
}
自定义序列化器
要为指定的 Content-Type
注册自定义序列化器,您需要调用 register
方法。以下示例中注册了两个自定义序列化器,用于反序列化 application/json
和 application/xml
数据:
install(ContentNegotiation) {
register(ContentType.Application.Json, CustomJsonConverter())
register(ContentType.Application.Xml, CustomXmlConverter())
}
接收和发送数据
创建数据类
要将接收到的数据反序列化为对象,您需要创建一个数据类,例如:
@Serializable
如果您使用 kotlinx.serialization,请确保此注解有 @Serializable
注解:
import kotlinx.serialization.*
import io.ktor.server.util.getValue
@Serializable
kotlinx.serialization 库支持以下类型的序列化/反序列化:
接收数据
要接收和转换请求内容,请调用接受数据类作为参数的 receive
方法:
post("/customer") {
val customer = call.receive<Customer>()
customerStorage.add(customer)
call.respondText("Customer stored correctly", status = HttpStatusCode.Created)
请求的 Content-Type
将用于选择序列化器来处理请求。以下示例展示了一个包含 JSON 或 XML 数据的示例 HTTP 客户端 请求,这些数据在服务器端被转换为 Customer
对象:
POST http://0.0.0.0:8080/customer
Content-Type: application/json
{
"id": 3,
"firstName": "Jet",
"lastName": "Brains"
}
POST http://0.0.0.0:8080/customer
Content-Type: application/xml
<Customer id="3" firstName="Jet" lastName="Brains"/>
您可以在这里找到完整示例:json-kotlinx。
发送数据
要在响应中传递数据对象,您可以使用 respond
方法:
routing {
get("/customer/{id}") {
val id: Int by call.parameters
val customer: Customer = customerStorage.find { it.id == id }!!
call.respond(customer)
在这种情况下,Ktor 使用 Accept
头来选择所需的序列化器。您可以在这里找到完整示例:json-kotlinx。
实现自定义序列化器
在 Ktor 中,您可以编写自己的序列化器来序列化/反序列化数据。为此,您需要实现 ContentConverter 接口:
interface ContentConverter {
suspend fun serialize(contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any): OutgoingContent?
suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any?
}
请参阅 GsonConverter 类作为实现示例。