You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Ktor Client实现包装响应的自定义通用转换器?

Ktor Client 实现通用包装响应转换器,直接提取Data实体

需求背景

接口返回固定格式的包装响应,包含data、statusCode、message字段,当前调用时需要显式指定DataResponseContainer<实体类>来获取数据,希望简化为直接调用body<实体类>()拿到data对应的对象。

解决方案

通过自定义Ktor Client插件,拦截响应体并自动完成包装结构的解析,提取data字段后返回给调用方。以下是具体实现步骤:


1. 确保依赖配置

项目中需引入Ktor序列化相关依赖(以Gradle为例):

dependencies {
    implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
    implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
}

(注:需将$ktorVersion和$serializationVersion替换为项目使用的对应版本)

2. 修正实体类序列化注解

确保所有涉及的实体类都添加@Serializable注解:

@Serializable
data class DataResponseContainer<out T>(
    val data: T?, 
    val statusCode: Int?, 
    val message: String?
)

@Serializable
data class SplashEntity(val lang: String, val type: String)

@Serializable
data class LoginEntity(val id: Int, val role: String)

3. 实现自定义响应转换插件

利用Ktor的transformResponseBody钩子完成自动解析:

import io.ktor.client.plugins.createClientPlugin
import io.ktor.client.request.HttpResponse
import io.ktor.http.isSuccess
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.readRemaining
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer

val DataTransformationPlugin = createClientPlugin("DataTransformationPlugin") {
    transformResponseBody { response, content, requestedType ->
        if (!response.status.isSuccess()) return@transformResponseBody content

        return@transformResponseBody try {
            // 读取原始响应内容
            val rawContent = content.readRemaining().readText()
            // 构造包装类的泛型序列化器
            val containerSerializer = DataResponseContainer.serializer(requestedType)
            // 反序列化到包装对象
            val container = Json.decodeFromString(containerSerializer, rawContent)
            
            // 提取data,若为null可根据业务需求调整处理逻辑
            val data = container.data ?: throw IllegalStateException("Response data is null")
            
            // 将data重新序列化为ByteReadChannel返回
            val dataJson = Json.encodeToString(requestedType.kotlinType!!.serializer(), data)
            ByteReadChannel(dataJson)
        } catch (e: Exception) {
            // 反序列化失败时返回原始内容
            content
        }
    }
}

4. 配置HttpClient并启用插件

创建客户端时同时安装ContentNegotiation和自定义插件:

import io.ktor.client.HttpClient
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.contentnegotiation.json
import io.ktor.serialization.json.Json

val httpClient = HttpClient(OkHttp) {
    install(ContentNegotiation) {
        json(Json {
            ignoreUnknownKeys = true // 忽略接口返回的未知字段
            encodeDefaults = false
        })
    }
    install(DataTransformationPlugin)
}

5. 简化调用方式

现在可以直接通过body<实体类>()获取data内容:

val splashEntity: SplashEntity = httpClient.get("splash").body()
val loginEntity: LoginEntity = httpClient.get("login").body()

可选优化

  • 错误处理增强:若响应statusCode非成功(如400、500),可解析message字段并抛出自定义业务异常,替代直接返回原始内容。
  • 空值处理:根据业务需求,data为null时可返回null而非抛出异常,只需修改为container.data ?: ByteReadChannel("null")(需确保序列化配置支持null值解析)。

内容的提问来源于stack exchange,提问作者Reprator

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:32:22