Ktor Server 中的内容协商与序列化
必需的依赖项: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 构件:
请注意,特定格式的序列化程序需要额外的构件。例如,kotlinx.serialization 需要 ktor-serialization-kotlinx-json 依赖项来处理 JSON。
序列化
在使用 kotlinx.serialization 转换器之前,您需要按照 安装 (Setup) 部分的说明添加 Kotlin 序列化插件。
JSON
要序列化/反序列化 JSON 数据,您可以选择以下库之一:kotlinx.serialization、Gson 或 Jackson。
在构建脚本中添加 ktor-serialization-kotlinx-json 构件:
在构建脚本中添加 ktor-serialization-gson 构件:
在构建脚本中添加 ktor-serialization-jackson 构件:
XML
要序列化/反序列化 XML,请在构建脚本中添加 ktor-serialization-kotlinx-xml:
请注意,
jsNode目标不支持 XML 序列化。
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()) // 支持 java.time.* 类型
}
}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())
}接收与发送数据
创建数据类
要将接收到的数据反序列化为对象,您需要创建一个数据类,例如:
data class Customer(val id: Int, val firstName: String, val lastName: String)如果您使用 kotlinx.serialization,请确保该类具有 @Serializable 注解:
import kotlinx.serialization.*
@Serializable
data class Customer(val id: Int, val firstName: String, val lastName: String)kotlinx.serialization 库支持以下类型的序列化/反序列化:
- 内置类
- 序列 (Sequences) 的反序列化
- 流 (Flows) 的序列化
接收数据
要接收并转换请求内容,请调用接受数据类作为参数的 receive 方法:
post("/customer") {
val customer = call.receive<Customer>()
customerStorage.add(customer)
call.respondText("Customer stored correctly", status = HttpStatusCode.Created)请求的 Content-Type 将被用于选择序列化程序以处理请求。下面的示例展示了一个示例 HTTP 客户端请求,其中包含在服务器端转换为 Customer 对象的 JSON 或 XML 数据:
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 类作为实现示例。
