如何为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
相关产品推荐
相关产品推荐

