Skip to content
Server Plugin

Ktor Server 中的內容協商與序列化

必要相依性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 構件:

Kotlin
Groovy
XML

請注意,特定格式的序列化程式需要額外的構件。例如,kotlinx.serialization 需要 ktor-serialization-kotlinx-json 相依性來處理 JSON。

序列化

在開始使用 kotlinx.serialization 轉換器之前,您需要按照 Setup 章節中的說明新增 Kotlin 序列化外掛程式。

JSON

要序列化/反序列化 JSON 資料,您可以選擇以下程式庫之一:kotlinx.serialization、Gson 或 Jackson。

在建置指令碼中新增 ktor-serialization-kotlinx-json 構件:

Kotlin
Groovy
XML

在建置指令碼中新增 ktor-serialization-gson 構件:

Kotlin
Groovy
XML

在建置指令碼中新增 ktor-serialization-jackson 構件:

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())  // 支援 java.time.* 型別
    }
}

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
data class Customer(val id: Int, val firstName: String, val lastName: String)

如果您使用 kotlinx.serialization,請確保該類別具有 @Serializable 註解:

kotlin
import kotlinx.serialization.*
@Serializable
data class Customer(val id: Int, val firstName: String, val lastName: String)

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 類別作為實作範例。