Skip to content
Server Plugin

Ktor 服务器中的内容协商与序列化

所需依赖项: io.ktor:ktor-server-content-negotiation

代码示例: json-kotlinx

原生服务器
Ktor 支持 Kotlin/Native,并允许您无需额外的运行时或虚拟机即可运行服务器。
支持: ✅

ContentNegotiation 插件有两个主要目的:

  • 在客户端和服务器之间协商媒体类型。为此,它使用 AcceptContent-Type 头。
  • 以特定格式序列化/反序列化内容。Ktor 开箱即用地支持以下格式:JSON、XML、CBOR 和 ProtoBuf。

在客户端,Ktor 提供了 ContentNegotiation 插件用于序列化/反序列化内容。

添加依赖项

ContentNegotiation

要使用 ContentNegotiation,您需要在构建脚本中包含 ktor-server-content-negotiation artifact:

Kotlin
Groovy
XML

请注意,特定格式的序列化器需要额外的 artifact。例如,kotlinx.serialization 需要 ktor-serialization-kotlinx-json 依赖项来处理 JSON。

序列化

在使用 kotlinx.serialization 转换器之前,您需要按照 设置 部分的说明添加 Kotlin 序列化插件。

JSON

要序列化/反序列化 JSON 数据,您可以选择以下库之一:kotlinx.serialization、Gson 或 Jackson。

在构建脚本中添加 ktor-serialization-kotlinx-json artifact:

Kotlin
Groovy
XML

在构建脚本中添加 ktor-serialization-gson artifact:

Kotlin
Groovy
XML

在构建脚本中添加 ktor-serialization-jackson artifact:

Kotlin
Groovy
XML

XML

要序列化/反序列化 XML,请在构建脚本中添加 ktor-serialization-kotlinx-xml

Kotlin
Groovy
XML

请注意,XML 序列化 不支持 jsNode 目标平台

CBOR

要序列化/反序列化 CBOR,请在构建脚本中添加 ktor-serialization-kotlinx-cbor

Kotlin
Groovy
XML

ProtoBuf

要序列化/反序列化 ProtoBuf,请在构建脚本中添加 ktor-serialization-kotlinx-protobuf

Kotlin
Groovy
XML

安装 ContentNegotiation

要将 ContentNegotiation 插件安装到应用程序中, 请将其传递给指定

模块
模块允许您通过分组路由来组织应用程序。
中的 install 函数。 以下代码片段展示了如何安装 ContentNegotiation ...

  • ... 在 embeddedServer 函数调用内部。
  • ... 在显式定义的 module 内部,它是 Application 类的扩展函数。
kotlin
kotlin

配置序列化器

Ktor 开箱即用地支持以下格式:JSONXMLCBOR。您还可以实现自己的自定义序列化器。

JSON 序列化器

要在应用程序中注册 JSON 序列化器,请调用 json 方法:

kotlin
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*

install(ContentNegotiation) {
    json()
}

json 方法还允许您调整 JsonBuilder 提供的序列化设置,例如:

kotlin

    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
        })

要在应用程序中注册 Gson 序列化器,请调用 gson 方法:

kotlin
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.gson.*

install(ContentNegotiation) {
    gson()
}

gson 方法还允许您调整 GsonBuilder 提供的序列化设置,例如:

kotlin
install(ContentNegotiation) {
    gson {
        registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
        setDateFormat(DateFormat.LONG, DateFormat.SHORT)
        setPrettyPrinting()
    }

要在应用程序中注册 Jackson 序列化器,请调用 jackson 方法:

kotlin
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.jackson.*

install(ContentNegotiation) {
    jackson()
}

jackson 方法还允许您调整 ObjectMapper 提供的序列化设置,例如:

kotlin
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 方法:

kotlin
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*

install(ContentNegotiation) {
    xml()
}

xml 方法还允许您访问 XML 序列化设置,例如:

kotlin
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 方法:

kotlin
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*

install(ContentNegotiation) {
    cbor()
}

cbor 方法还允许您访问 CborBuilder 提供的 CBOR 序列化设置,例如:

kotlin
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 方法:

kotlin
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*

install(ContentNegotiation) {
    protobuf()
}

protobuf 方法还允许您访问 ProtoBufBuilder 提供的 ProtoBuf 序列化设置,例如:

kotlin
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/jsonapplication/xml 数据:

kotlin
install(ContentNegotiation) {
    register(ContentType.Application.Json, CustomJsonConverter())
    register(ContentType.Application.Xml, CustomXmlConverter())
}

接收和发送数据

创建数据类

要将接收到的数据反序列化为对象,您需要创建一个数据类,例如:

kotlin
@Serializable

如果您使用 kotlinx.serialization,请确保此注解有 @Serializable 注解:

kotlin
import kotlinx.serialization.*
import io.ktor.server.util.getValue

@Serializable

kotlinx.serialization 库支持以下类型的序列化/反序列化:

接收数据

要接收和转换请求内容,请调用接受数据类作为参数的 receive 方法:

kotlin

        post("/customer") {
            val customer = call.receive<Customer>()
            customerStorage.add(customer)
            call.respondText("Customer stored correctly", status = HttpStatusCode.Created)

请求的 Content-Type 将用于选择序列化器来处理请求。以下示例展示了一个包含 JSON 或 XML 数据的示例 HTTP 客户端 请求,这些数据在服务器端被转换为 Customer 对象:

HTTP
POST http://0.0.0.0:8080/customer
Content-Type: application/json

{
  "id": 3,
  "firstName": "Jet",
  "lastName": "Brains"
}
HTTP
POST http://0.0.0.0:8080/customer
Content-Type: application/xml

<Customer id="3" firstName="Jet" lastName="Brains"/>

您可以在这里找到完整示例:json-kotlinx

发送数据

要在响应中传递数据对象,您可以使用 respond 方法:

kotlin
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 接口:

kotlin
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 类作为实现示例。