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

Android中如何从Retrofit的Authenticator中获取服务端返回的错误响应体

可行实现方案

核心原理

OkHttp 提供的peekBody()方法可以拷贝一份Response Body流,读取拷贝后的流不会影响原Response的后续使用,完美解决Authenticator中读取错误信息后原请求Body为空的问题。


具体实现步骤

1. 安全读取错误响应

通过peekBody(Long.MAX_VALUE)获取完整的响应体拷贝,再解析为后端约定的错误结构:

// 读取拷贝后的响应体,不消耗原Response的Body
val errorBody = response.peekBody(Long.MAX_VALUE).string()
// 解析为你项目约定的错误实体类,示例用Gson解析
val errorInfo = Gson().fromJson(errorBody, ApiError::class.java)

ApiError为项目自定义的后端错误响应对应实体类,通常包含错误码、错误信息等字段。

2. 完整Authenticator代码示例

class TokenAuthenticator : Authenticator {
    // 约定的后端错误码
    private val TOKEN_EXPIRED = 10001
    private val NEED_RELOGIN = 10002
    private val MAX_RETRY_COUNT = 2

    override fun authenticate(route: Route?, response: Response): Request? {
        // 防止无限重试
        if (response.responseCount >= MAX_RETRY_COUNT) {
            return null
        }

        // 读取错误信息
        val errorBody = try {
            response.peekBody(Long.MAX_VALUE).string()
        } catch (e: Exception) {
            // 读取失败直接不处理
            return null
        }

        val errorInfo = try {
            Gson().fromJson(errorBody, ApiError::class.java)
        } catch (e: Exception) {
            // 解析失败直接不处理
            return null
        }

        return when (errorInfo.code) {
            // Access Token过期,执行同步刷新逻辑
            TOKEN_EXPIRED -> {
                val newToken = syncRefreshToken()
                if (newToken.isNullOrEmpty()) {
                    // 刷新失败,终止请求
                    null
                } else {
                    // 携带新Token构建新请求返回,自动重试
                    response.request.newBuilder()
                        .removeHeader("Authorization")
                        .addHeader("Authorization", "Bearer $newToken")
                        .build()
                }
            }
            // Refresh Token过期/账号异常,终止请求跳转登录
            NEED_RELOGIN -> {
                // 可在此处发送全局事件通知页面跳登录
                null
            }
            // 其他错误不处理
            else -> null
        }
    }

    /**
     * 同步刷新Token的实现,注意不能用异步逻辑
     */
    private fun syncRefreshToken(): String? {
        // 此处实现你的同步刷新Token逻辑,比如直接用OkHttp调用刷新接口
        return "new_access_token"
    }

    // 示例错误实体类
    data class ApiError(val code: Int, val message: String)
}

注意事项

  • 不要直接调用response.body?.string()读取响应,会消耗原流导致后续Retrofit拿到空Body
  • 刷新Token的逻辑必须为同步实现,Authenticator的authenticate方法运行在后台线程,不支持异步回调
  • 只有HTTP状态码为401的响应才会触发Authenticator,需确保后端Token过期时返回401状态码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 02:06:02