我正在处理一个应用程序,当它收到来自GraphQL的未经身份验证的错误时,需要在后台刷新身份验证令牌。我以前使用OkHttp拦截器为普通API实现了此功能,并将一些逻辑移植到了ApolloInterceptor。
我试图查看内部的Apollo拦截器源代码,以了解它们的工作原理,并设法制定了可行的解决方案。但是,因为这是我第一次使用apollo-android库,所以我想征询一些反馈意见,也许我忽略了某些事情。
class ApolloRefreshTokenInterceptor(
private val tokenService: TokenService,
private val tokenStorage: TokenStorage
) : ApolloInterceptor {
@Volatile
private var disposed = false
@Volatile
private var retryCount = 0
override fun interceptAsync(request: ApolloInterceptor.InterceptorRequest, chain: ApolloInterceptorChain, dispatcher: Executor, callBack: ApolloInterceptor.CallBack) {
chain.proceedAsync(request, dispatcher, object : ApolloInterceptor.CallBack {
override fun onFailure(e: ApolloException) {
e.printStackTrace()
callBack.onFailure(e)
}
override fun onResponse(response: ApolloInterceptor.InterceptorResponse) {
if (disposed) return
if (isUnauthenticated(response)) {
// Get current tokens before refreshing.
val currentTokens = tokenStorage.get()
Log.d("GRAPHQL", "User unauthenticated")
synchronized(this) {
if (retryCount >= MAX_RETRY_COUNT) {
Log.d("GRAPHQL", "Retry count reached limit.")
callBack.onResponse(response)
callBack.onCompleted()
retryCount = 0
return
}
retryCount++
// Check if the token was not already refreshed.
if (wasTokenAlreadyRefreshed(currentTokens)) {
Log.d("GRAPHQL", "Token already refreshed.")
return chain.proceedAsync(
request
.toBuilder()
.fetchFromCache(false)
.build(),
dispatcher,
this
)
}
// Refresh token sync.
val result = runBlocking {
Log.d("GRAPHQL", "Refreshing token.")
refreshToken()
}
if (result.isSuccess()) {
Log.d("GRAPHQL", "Tokens refreshed")
// Remake request.
chain.proceedAsync(
request
.toBuilder()
.fetchFromCache(false)
.build(),
dispatcher,
this
)
return
} else {
Log.d("GRAPHQL", "Raise event cannot refresh token")
// Raise UserUnauthenticatedEvent to be handled by upper layer.
// We cannot refresh the session automatically, user needs to login again.
raiseUnAuthenticatedUserEvent()
callBack.onResponse(response)
callBack.onCompleted()
return
}
}
} else {
retryCount = 0
callBack.onResponse(response)
callBack.onCompleted()
}
}
override fun onFetch(sourceType: ApolloInterceptor.FetchSourceType?) {
callBack.onFetch(sourceType)
}
override fun onCompleted() {
}
})
}
override fun dispose() {
disposed = true
}
private fun isUnauthenticated(response: ApolloInterceptor.InterceptorResponse): Boolean {
return if (response.parsedResponse.isPresent) {
@Suppress("UNCHECKED_CAST")
val errors = response.parsedResponse.get().errors as List<Error>?
errors?.find { it.message == "authentication required" } != null
} else false
}
private suspend fun refreshToken(): Result<Boolean> = tokenService.refresh()
/**
* Check if authentication tokens are different. If they are different it means another
* thread already refreshed the access tokens.
*/
private fun wasTokenAlreadyRefreshed(authTokens: TokenStorage.AuthTokens?): Boolean =
authTokens != tokenStorage.get()
private fun raiseUnAuthenticatedUserEvent() {
// TODO raise event
}
}
这些是我考虑的前提条件。
这是正确的方法还是执行这种操作,或者有其他选择吗?
谢谢