KMM项目Ktor客户端iOS端刷新token后不重试原请求如何解决
KMM Ktor iOS端Auth刷新令牌后不重试问题修复方案
核心原因
该问题是iOS端Ktor 1.x版本特性、协程调度差异和代码实现问题共同导致的,Android端因为JVM协程调度逻辑和Ktor引擎实现不同所以能正常运行。
常见触发点:
- refresh令牌的接口未正确封装为Kotlin协程挂起函数,回调未执行就提前返回了空令牌
- refresh接口误用了带Auth插件的HttpClient发起请求,陷入401校验死循环
- 未显式配置bearer认证的
loadTokens和sendWithoutRequest参数,iOS端无法识别新令牌 - 令牌存储在Auth配置的局部变量中,刷新后的令牌无法被重试请求读取
修复步骤
1. 封装refresh接口为标准挂起函数
确保iOS端的refreshAuthToken方法用suspendCancellableCoroutine包装,协程会等待接口返回结果再往下执行,避免提前返回未初始化的令牌。
示例封装代码:
suspend fun refreshAuthToken(): Result<LoginResponse> = suspendCancellableCoroutine { cont -> // 必须用不带Auth插件的tokenClient调用refresh接口,避免死循环 tokenClient.post<LoginResponse>(ApiEndPoints.RefreshToken.url) { contentType(ContentType.Application.Json) setBody(RefreshTokenRequest(refreshToken = TokenStorage.getRefreshToken())) }.invokeOnCompletion { throwable -> if (throwable != null) { cont.resume(Result.failure(throwable)) return@invokeOnCompletion } val response = getCompleted() cont.resume(Result.success(response)) } }
2. 调整Auth插件配置
增加loadTokens配置,令牌统一存在跨平台KV存储中(可以使用multiplatform-settings或者DataStore实现),不要存在Auth配置的局部变量里,同时显式开启sendWithoutRequest参数。
修改后的iOS端HttpClient配置:
actual val httpClient: HttpClient = HttpClient { defaultRequest { host = ApiEndPoints.Base.url url { protocol = URLProtocol.HTTPS } contentType(ContentType.Application.Json) header(CONNECTION, CLOSE) } expectSuccess = false install(Auth) { bearer { // 从持久化存储读取当前有效令牌 loadTokens { val accessToken = TokenStorage.getAccessToken() val refreshToken = TokenStorage.getRefreshToken() if (accessToken.isNotEmpty() && refreshToken.isNotEmpty()) { BearerTokens(accessToken, refreshToken) } else null } // iOS端必须显式开启,否则新令牌不会自动携带到请求中 sendWithoutRequest = true refreshTokens { _ -> NSLog("Unauthorized response received") val refreshResult = BaseAPIClass().refreshAuthToken() return@refreshTokens refreshResult.fold( failed = { NSLog("Token refresh failed: ${it.message}") // 刷新失败清空令牌,触发登录逻辑 TokenStorage.clearTokens() null }, succeeded = { response -> NSLog("Token refresh success") // 刷新成功更新持久化存储的令牌 TokenStorage.saveTokens( accessToken = response.accessToken ?: "", refreshToken = response.refreshToken ?: "" ) BearerTokens( accessToken = response.accessToken ?: "", refreshToken = response.refreshToken ?: "" ) } ) } } } install(JsonFeature) { val json = kotlinx.serialization.json.Json { ignoreUnknownKeys = true coerceInputValues = true } serializer = KotlinxSerializer(json) } }
3. 可选升级方案
Ktor 1.x已经停止维护,2.x版本完全重构了iOS端的引擎和Auth插件逻辑,修复了大量跨平台兼容性问题,如果条件允许建议直接升级到Ktor 2.2.0以上版本,可避免该类问题。
内容的提问来源于stack exchange,提问作者Md Hanif
相关产品推荐
相关产品推荐

