Ktor调用API时出现-1未知状态码及ContentType:null的运行时修复求助
运行时修复Ktor客户端NoTransformationFoundException(状态码-1)问题
问题背景
调用API时偶尔出现以下响应日志:
13:12:23.142 System.out I HttpClient: RESPONSE: -1 Unknown Status Code 13:12:23.142 System.out I METHOD: HttpMethod(value=POST) 13:12:23.142 System.out I FROM: https://webservice.azurewebsites.net/en/Dispatch/getTickets 13:12:23.142 System.out I COMMON HEADERS 13:12:23.142 System.out I -> content-length: 0 13:12:23.142 System.out I -> date: Wed, 25 Sep 2024 07:42:23 GMT 13:12:23.142 System.out I -> server: Microsoft-IIS/10.0 13:12:23.142 System.out I -> set-cookie: ARRAffinity=6dec0997e21dec30ce7ef102ebaec80934d28e2723296e23b047a25a64c195a9;Path=/;HttpOnly;Secure;Domain=webservice.azurewebsites.net; ARRAffinitySameSite=6dec0997e21dec30ce7ef102ebaec80934d28e2723296e23b047a25a64c195a9;Path=/;HttpOnly;SameSite=None;Secure;Domain=webservice.azurewebsites.net 13:12:23.142 System.out I -> www-authenticate: Bearer error="invalid_token", error_description="The token expired at '09/25/2024 07:22:35'" 13:12:23.142 System.out I -> x-android-received-millis: 1727250143135 13:12:23.142 System.out I -> x-android-response-source: NETWORK 401 13:12:23.142 System.out I -> x-android-selected-protocol: http/1.1 13:12:23.142 System.out I -> x-android-sent-millis: 1727250142738 13:12:23.142 System.out I -> x-powered-by: ASP.NET 13:12:23.142 System.out I BODY Content-Type: null 13:12:23.142 System.out I BODY START 13:12:23.142 System.out I 13:12:23.142 System.out I BODY END
随后触发崩溃,日志如下:
Expected response body of the type 'class app.models.Response' but was 'class io.ktor.utils.io.ByteBufferChannel' In response from `https://webservice.azurewebsites.net/en/Dispatch/getTickets` Response status `-1 Unknown Status Code` Response header `ContentType: null` Request header `Accept: application/json` io.ktor.client.call.NoTransformationFoundException: Expected response body of the type 'class app.models.Response' but was 'class io.ktor.utils.io.ByteBufferChannel' In response from `https://webservice.azurewebsites.net/en/Dispatch/getTickets` Response status `-1 Unknown Status Code` Response header `ContentType: null` Request header `Accept: application/json` at io.ktor.client.call.HttpClientCall.bodyNullable(HttpClientCall.kt:94) at app.service.WebService$DispatchService.getTickets(WebService.kt:1088) at app.service.WebService$DispatchService$getTickets$1.invokeSuspend(Unknown Source:15) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115) at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
已尝试通过以下代码处理-1状态码并重置令牌,但未解决问题:
ResponseObserver { response -> if (response.status.value == -1 && response.contentType() == null ) { resetToken() } }
仅重启Android应用可临时解决,需实现运行时修复方案。
解决方案
1. 全局拦截异常,避免崩溃并触发重试
在Ktor客户端配置中添加响应异常处理器,捕获NoTransformationFoundException,针对状态码-1+ContentType=null的场景,触发重试逻辑:
// 自定义重试标记异常 class RetryRequestException : Exception() val httpClient = HttpClient(Android) { // 配置内容协商(确保已添加JSON转换器) install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } // 配置重试插件 install(Retry) { retryOnExceptionIf(3) { _, exception -> exception is RetryRequestException } } // 响应验证器,拦截异常 HttpResponseValidator { handleResponseException { exception -> if (exception is NoTransformationFoundException) { val call = exception.call if (call.response.status.value == -1 && call.response.contentType() == null) { throw RetryRequestException() } } } } // 其他配置:超时、日志等 }
2. 重置HttpClient实例(核心修复)
状态码-1通常意味着客户端网络实例处于异常状态(如连接池损坏、SSL会话异常),此时复用原有实例会持续触发问题,需重建实例:
// 单例管理HttpClient实例 object HttpClientManager { private var client: HttpClient? = null fun getClient(): HttpClient { return client ?: createNewClient().also { client = it } } fun resetClient() { // 关闭旧实例释放资源 client?.close() // 创建新实例 client = createNewClient() } private fun createNewClient(): HttpClient { return HttpClient(Android) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } // 复制原有客户端的所有配置 } } }
在异常拦截中调用重置方法:
HttpResponseValidator { handleResponseException { exception -> if (exception is NoTransformationFoundException) { val call = exception.call if (call.response.status.value == -1 && call.response.contentType() == null) { HttpClientManager.resetClient() throw RetryRequestException() } } } }
3. 强制指定响应类型转换
在请求时手动指定转换类型,避免Ktor因ContentType=null找不到转换器:
suspend fun getTickets(): Response? { return runCatching { HttpClientManager.getClient().post("https://webservice.azurewebsites.net/en/Dispatch/getTickets") { // 请求参数、header配置 headers { append("Accept", "application/json") } }.body<Response>() }.getOrElse { exception -> if (exception is NoTransformationFoundException) { HttpClientManager.resetClient() // 返回默认值或null,避免崩溃 null } else { throw exception } } }
总结
问题根源是Ktor客户端实例因底层网络异常进入无效状态,导致无法处理无ContentType的响应。通过异常拦截+重置客户端+重试的组合方案,可在运行时修复该问题,无需重启应用。
内容的提问来源于stack exchange,提问作者Osman Gani
相关产品推荐
相关产品推荐

