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:
請注意,XML 序列化 在
jsNode目標上不受支援。
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 程式庫支援以下型別的序列化/反序列化:
接收資料
要接收並轉換請求內容,請呼叫接受資料類別作為參數的 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 類別作為實作範例。
