如何用Ktor实现类似OkHttp的响应拦截及401自动重登录逻辑?
如何用Ktor实现类似OkHttp的响应拦截及401自动重登录逻辑?
看起来你已经在尝试用Ktor自定义插件实现401自动重登了,但遇到了请求序列化的坑对吧?别慌,咱们一步步拆解问题,把逻辑捋顺~
首先你碰到的java.lang.IllegalStateException: No request transformation found异常,本质是Ktor不知道怎么把你的LoginRequest对象转成HTTP请求体的字节流——你得先给Ktor配好内容序列化插件,它才能处理自定义对象的请求体序列化。
第一步:先搞定内容序列化的基础配置
比如用Jackson处理JSON序列化,你需要先在Client里安装ContentNegotiation插件,注册对应的序列化器:
install(ContentNegotiation) { Jackson { // 可以根据后端需求配置Jackson规则,比如忽略未知字段 configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) // 处理Kotlin数据类的序列化/反序列化 registerModule(KotlinModule()) } }
这一步是核心前提,没它的话Ktor根本认不出你的LoginRequest对象
第二步:修正你的AuthPlugin逻辑
你之前的代码有个逻辑误区:遇到401时直接修改原请求当登录请求,但原请求是调用业务接口的啊,登录得单独调登录API才对!正确的逻辑应该是:先发起独立的登录请求,登录成功后再重新调用原来的业务接口。
另外不能直接复用原请求对象,得用当前Client实例发起独立的登录请求,修改后的插件代码如下:
// 插件配置类 data class AuthPluginConfig( var contentType: ContentType = ContentType.Application.Json ) val AuthPlugin = createClientPlugin("AuthPlugin", ::AuthPluginConfig) { val contentType: ContentType = pluginConfig.contentType val currentClient = this@createClientPlugin // 获取当前Client实例用于发起登录请求 on(Send) { originalRequest -> val originalCall = proceed(originalRequest) // 检查响应是否为401未授权 if (originalCall.response.status == HttpStatusCode.Unauthorized) { // 1. 获取存储的登录凭证 val username = credentialsManager.getString("username", "") val password = credentialsManager.getString("password", "") if (username.isBlank() || password.isBlank()) { // 没有可用凭证,直接返回原401响应 return@on originalCall } // 2. 发起独立的登录请求(替换成你实际的登录接口地址) val loginRequest = LoginRequest(username, password) val loginCall = currentClient.post("https://your-domain.com/api/login") { contentType(contentType) setBody(loginRequest) } // 3. 检查登录是否成功 return@on if (loginCall.response.status.isSuccess()) { // 登录成功:这里可以把新获取的token(从loginCall响应里取)存到credentialsManager // 然后重新发起原来的业务请求 proceed(originalRequest) } else { // 登录失败,返回原401响应 originalCall } } else { // 非401情况,直接返回原响应 originalCall } } }
几个关键修改点说明
- 别拿原请求凑数:原请求是调业务接口的,登录必须单独调登录API,用当前Client发起独立请求才是正确姿势
- 序列化是基础:配好
ContentNegotiation后,Ktor就能自动把LoginRequest转成对应格式的请求体,彻底解决你碰到的转换异常 - 处理边界情况:比如存储的账号密码过期,这时候要及时终止重登逻辑,避免无限循环
- 更新凭证很重要:登录成功后记得把新token存回
credentialsManager,后续请求才能用新凭证授权
备注:内容来源于stack exchange,提问作者Roony
相关产品推荐
相关产品推荐

