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
:
請注意,
jsNode
目標不支援 XML 序列化。
CBOR
要序列化/反序列化 CBOR,請在建構指令稿中新增 ktor-serialization-kotlinx-cbor
:
ProtoBuf
要序列化/反序列化 ProtoBuf,請在建構指令稿中新增 ktor-serialization-kotlinx-protobuf
:
安裝 ContentNegotiation
要安裝 ContentNegotiation
外掛程式到應用程式, 請將其傳遞給指定
install
函數。 以下程式碼片段展示如何安裝 ContentNegotiation
... - ... 在
embeddedServer
函數呼叫內部。 - ... 在明確定義的
module
內部,該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 類別作為實作範例。