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

Ktor validateResponse读取响应体触发ContentConvertException问题求助

Ktor validateResponse读取响应体引发异常的解决方案

问题

使用Ktor开发Android应用,在validateResponse中校验响应体的status字段,当该字段值为"301"时抛出自定义异常。但调用response.body<xxxErrorResponse?>()时触发io.ktor.serialization.ContentConvertException,且仅在非301场景下出现该错误,推测是响应体被消费后无法再次读取导致。

错误原因

  1. 响应流被一次性消费:Ktor的response.body()方法会读取并消费响应流,后续任何尝试读取响应体的操作都会失败。
  2. 默认校验逻辑拦截:expectSuccess = true时,非2xx响应会被Ktor默认校验逻辑拦截并抛出异常,自定义校验逻辑可能无法执行。
  3. 反射缺失:错误信息提到"Kotlin reflection is not available",说明缺少kotlin-reflect依赖,导致序列化解析失败。

解决方法

1. 禁用默认成功校验,手动处理响应状态

将expectSuccess设为false,避免Ktor默认逻辑提前拦截非2xx响应,确保自定义校验逻辑能执行。

2. 缓存响应体字节,避免流被消费

先读取响应体的所有字节并缓存,再将字节重新封装为ByteReadChannel设置回response.content,保证后续可多次读取。

3. 直接解析缓存的字节,替代response.body()

使用Json.decodeFromString直接解析缓存的字节数组,避免消费原始响应流。

4. 添加kotlin-reflect依赖

确保项目中包含kotlin-reflect依赖,解决序列化时的反射问题。

修改后的代码示例

object ApiHttpClient {
    internal fun <T> create(
        envVar: EnvVar,
        dataStore: UserDataStore,
        engineFactory: HttpClientEngineFactory<T>,
        block: T.() -> Unit = {},
    ): HttpClient where T : HttpClientEngineConfig = HttpClient(engineFactory = engineFactory) {
        engine(block)
        install(ContentNegotiation) {
            json(
                json = Json {
                    serializersModule = SerializersModule {
                        contextual(InstantSerializer)
                        contextual(LocalDateSerializer)
                    }
                    ignoreUnknownKeys = true
                    coerceInputValues = true
                    useAlternativeNames = false
                },
                contentType = ContentType.Application.Json,
            )
        }

        // 禁用默认成功校验,手动处理响应状态
        expectSuccess = false

        HttpResponseValidator {
            validateResponse { response ->
                // 读取响应体字节并缓存
                val responseBytes = response.content.readBytes()
                // 重置响应流,供后续操作使用
                response.content = ByteReadChannel(responseBytes)

                // 解析响应体,捕获解析异常
                val jsonResponseBody = try {
                    Json.decodeFromString<xxxErrorResponse?>(String(responseBytes))
                } catch (_: Exception) {
                    null
                }

                // 校验status字段,抛出301自定义异常
                if (jsonResponseBody?.status == "301") {
                    throw ApiException.HttpResponse301(cause = jsonResponseBody.status)
                }

                // 手动校验2xx响应,替代expectSuccess=true的默认行为
                if (!response.status.isSuccess()) {
                    throw ClientRequestException(response, "请求失败,状态码:${response.status.value}")
                }
            }

            // 处理自定义异常
            handleResponseException { exception ->
                if (exception is ApiException.HttpResponse301) {
                    // 这里可以添加301对应的业务逻辑,比如跳转更新页面
                    throw exception
                }
                throw exception
            }
        }
    }
}

依赖补充

在项目的build.gradle(Module级别)中添加kotlin-reflect依赖:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-reflect:${kotlin_version}"
    // 其他依赖...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 19:57:51