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

請注意,jsNode 目標不支援 XML 序列化。

CBOR

要序列化/反序列化 CBOR,請在建構指令稿中新增 ktor-serialization-kotlinx-cbor

Kotlin
Groovy
XML

ProtoBuf

要序列化/反序列化 ProtoBuf,請在建構指令稿中新增 ktor-serialization-kotlinx-protobuf

Kotlin
Groovy
XML

安裝 ContentNegotiation

安裝 ContentNegotiation 外掛程式到應用程式, 請將其傳遞給指定

模組
模組允許您透過分組路由來組織應用程式。
中的 install 函數。 以下程式碼片段展示如何安裝 ContentNegotiation ...

  • ... 在 embeddedServer 函數呼叫內部。
  • ... 在明確定義的 module 內部,該 moduleApplication 類別的擴充函數。
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 類別作為實作範例。