okhttp Authenticator请求重试不起作用



你好,我们在身份验证系统中使用okhttp/retrofitoauth2,实际上,当api端点返回UNAUTHORIZED时,Authenticator会被触发,我们成功地获得了新凭据,但问题是身份验证程序没有重试之前返回UNAUUTHORIZED的端点,这就是我所做的:

@Singleton
class RefreshTokenAuthenticator @Inject constructor(
@Named(CURRENT_CREDENTIALS_PREFERENCES_NAME)
private val userDataPreferences: SharedPreferences
) : okhttp3.Authenticator {
var apiHolder: ApiHolder? = null
private val json = Json { ignoreUnknownKeys = true }
override fun authenticate(route: Route?, response: Response): Request? {
val credentialsString = userDataPreferences.getString(
USER_CREDENTIALS_KEY, USER_CREDENTIALS_KEY
) ?: ""
var request: Request? = null
val credentials: UserCredentials? = try {
json.decodeFromString(
UserCredentials.serializer(), credentialsString
)
} catch (e: SerializationException) {
null
}
credentials?.let { userCredentials ->
CoroutineScope(Dispatchers.IO).launch {
try {
val refreshTokenResponse = apiHolder?.api?.refreshToken(userCredentials)
if (refreshTokenResponse?.isSuccessful == true) {
val responseBody = refreshTokenResponse.body()?.data
responseBody?.let { body ->
val userCredentialsJson = json.encodeToString(
UserCredentials.serializer(),
body
)
userDataPreferences.edit().putString(
USER_CREDENTIALS_KEY,
userCredentialsJson
).apply()
request = response.request
.newBuilder()
.header("Authorization", "Bearer ${body.accessToken}")
.build()
}
} else {
request = null
//TODO: handle errors
}
} catch (e: Exception) {
Timber.e(e)
}
}
}
return request
}
}

这就是我创建OkhttpClient:的方法

////
return OkHttpClient.Builder()
.addInterceptor(defaultHeadersInterceptor)
.addInterceptor(authorizationInterceptor)
.addInterceptor(errorHandlingInterceptor)
.addInterceptor(loggingInterceptor)
.addInterceptor(CurlInterceptor {
Timber.d("Ok2Curl $it ")
})
.authenticator(refreshTokenAuthenticator)
.build()

注意:如果需要,授权拦截器会在每个请求中添加Bearer令牌。

看来您需要考虑API调用和重试代码,而拦截器机制可能无法满足您的需要。

我的API客户端展示了一种替代方法,并封装了okhttp,这样Android视图模型就可以通过一行代码进行API调用,而不是直接使用okhttp。

正如Gary Archer所提到的,你可以做些什么来完成未授权请求的重试策略,只需像之前的请求一样添加另一个拦截器,并让它们在一段时间后重复你的调用-重试策略会是什么样子完全取决于你,但我可以给你举一个可以做什么的小例子:

class RetryOnUnauthorizedInterceptor @Inject constructor(
@DispatcherDefault private val dispatcher: CoroutineDispatcher
) : Interceptor {
constructor() : this(Dispatchers.Default)
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val responseCode = response.code
return if (responseCode == HTTP_UNAUTHORIZED) {
retry(chain, request, response)
} else response
}
private fun retry(chain: Interceptor.Chain, request: Request, previousResponse: Response): Response {
var response = previousResponse
var retryCount = 0
val responseCodes = mutableListOf(response.code)
do {
response.close()
runBlocking(dispatcher) {
delay(RETRY_DELAY_MILLIS)
}
response = chain.proceed(request)
responseCodes.add(response.code)
retryCount++
} while (response.code == HTTP_UNAUTHORIZED && retryCount < MAX_RETRY_COUNT)
return response
}
companion object {
private const val RETRY_DELAY_MILLIS = 2000L
private const val MAX_RETRY_COUNT = 2
private const val HTTP_UNAUTHORIZED = 401
}
}

这里的这个是用Coroutines完成的,但您可以根据异步实现来调整它。

编码快乐!

最新更新