Как отменить запрос в interceptor android retrofit?
Имеется такой интерцептор:
class AuthToken(context: Context) : Interceptor {
var cont = context
override fun intercept(chain: Interceptor.Chain): Response {
val sp = cont.getSharedPreferences("app_data", 0)
val originalRequest:Request? = (if (sp!!.getLong("expires_in", 0) - sp.getLong("time_delta", 0) - System.currentTimeMillis() / 1000 <= 60) {
updateToken(chain)
null
} else {
chain.request()
.newBuilder()
.header("Content-type:", "application/json")
.header("Authorization", "Bearer " + cont.getSharedPreferences("app_data", 0).getString("access_token", "")!!)
.build()
})
if (originalRequest!=null){
val response = chain.proceed(originalRequest)
when (response.code) {
401 -> {
updateToken(chain)
}
500 -> {
Toast.makeText(cont, cont.getString(R.string.server_error_500), Toast.LENGTH_SHORT).show()
}
}
return response
}else chain.proceed(originalRequest)
}
private fun updateToken(request: Interceptor.Chain) {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val dispatcher = Dispatcher()
dispatcher.maxRequests = 1
val client = OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(100, TimeUnit.SECONDS)
.dispatcher(dispatcher)
.readTimeout(100, TimeUnit.SECONDS).build()
client.dispatcher.cancelAll()
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
val api = retrofit.create(APIService::class.java)
api.getNewToken(ReqAccessToken(cont.getSharedPreferences("app_data", 0).getString("refresh_token", "")!!)).enqueue(object : Callback<ResNewTokens> {
override fun onResponse(call: Call<ResNewTokens>, response: retrofit2.Response<ResNewTokens>) {
if (response.isSuccessful) {
val editor = cont.getSharedPreferences("app_data", 0).edit()
editor.putString("access_token", Objects.requireNonNull<ResNewTokens>(response.body()).access_token).apply()
editor.putString("refresh_token", Objects.requireNonNull<ResNewTokens>(response.body()).refresh_token).apply()
editor.putLong("expires_in", response.body()!!.expires_in!!).apply()
makeNewCall(request)
} else {
when (response.code()) {
401 -> {
Singleton.logOut(cont)
}
500 -> {
Toast.makeText(cont, cont.getString(R.string.server_error_500), Toast.LENGTH_SHORT).show()
}
}
}
}
override fun onFailure(call: Call<ResNewTokens>, t: Throwable) {
}
})
}
private fun makeNewCall(req: Interceptor.Chain): Request {
return req.request()
.newBuilder()
.header("Content-type:", "application/json")
.header("Authorization", "Bearer " + cont.getSharedPreferences("app_data", 0).getString("access_token", "")!!)
.build()
}
}
проблема в этих строках:
if (sp!!.getLong("expires_in", 0) - sp.getLong("time_delta", 0) - System.currentTimeMillis() / 1000 <= 60) {
updateToken(chain)
null
}
суть задачи которую я пытаюсь решить - убрать лишний запрос с протухшим токеном который заведомо точно вернет 401 ошибку. Для этого я обернул все в if но не получается нормально это реализовать так как функция intercept должна полюбому возвращать Response чего не делается в данном условии. Думал сделать чтобы null слался но получается криво. Нужно как-то либо отменить запрос который планировался к посланию либо как-то иначе решить данную проблему но я не придумал как именно :(
UPDATE:
Вот что пока получилось:
override fun intercept(chain: Interceptor.Chain): Response {
val sp = cont.getSharedPreferences("app_data", 0)
val originalRequest = if (sp!!.getLong("expires_in", 0) - sp.getLong("time_delta", 0) - System.currentTimeMillis() / 1000 <= 60) {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val dispatcher = Dispatcher()
dispatcher.maxRequests = 1
val client = OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(100, TimeUnit.SECONDS)
.dispatcher(dispatcher)
.readTimeout(100, TimeUnit.SECONDS).build()
client.dispatcher.cancelAll()
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
val api = retrofit.create(APIService::class.java)
synchronized(this) {
when {
api.getNewToken(ReqAccessToken(cont.getSharedPreferences("app_data", 0).getString("refresh_token", "")!!)).execute().isSuccessful -> {
val responseBody = api.getNewToken(ReqAccessToken(cont.getSharedPreferences("app_data", 0).getString("refresh_token", "")!!)).execute().body()
val editor = cont.getSharedPreferences("app_data", 0).edit()
editor.putString("access_token", Objects.requireNonNull<ResNewTokens>(responseBody).access_token).apply()
editor.putString("refresh_token", Objects.requireNonNull<ResNewTokens>(responseBody).refresh_token).apply()
editor.putLong("expires_in", responseBody!!.expires_in!!).apply()
chain.request()
.newBuilder()
.header("Content-type:", "application/json")
.header("Authorization", "Bearer " + cont.getSharedPreferences("app_data", 0).getString("access_token", "")!!)
.build()
}
else->{
}
}
}
} else {
chain.request()
.newBuilder()
.header("Content-type:", "application/json")
.header("Authorization", "Bearer " + cont.getSharedPreferences("app_data", 0).getString("access_token", "")!!)
.build()
}
val response = chain.proceed(originalRequest)
when (response.code) {
401 -> {
//updateToken(chain)
}
500 -> {
Toast.makeText(cont, cont.getString(R.string.server_error_500), Toast.LENGTH_SHORT).show()
}
}
return response
}
и val response = chain.proceed(originalRequest) говорит о том что ждут request. Я то пробовал в if прописать request но его тогда не видно я так понял.
Ответы (1 шт):
У вас должно получиться что-то вроде такого:
//network
val logger = object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Timber.tag("OkHttp").d(message)
}
}
val loggingInterceptor = HttpLoggingInterceptor(logger)
.apply {
level = HttpLoggingInterceptor.Level.BODY
}
val okHttpClientCommon = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
val authRetrofit = Retrofit.Builder()
.baseUrl(BuildConfig.API_IP + BuildConfig.API_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.client(okHttpClientCommon)
.build()
val authApi = authRetrofit.create(AuthApi::class.java)
val accessTokenInterceptor = object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
//we need to set header in any case, as we need to receive JSON response
//instead of redirect to `/login/ page
val token = sessionRepository.getAccessToken() ?: ""
val request =
chain
.request()
.newBuilder()
.header(
ScpConstants.Api.HEADER_AUTHORIZATION,
ScpConstants.Api.HEADER_PART_BEARER + token
)
.build()
return chain.proceed(request)
}
}
val unauthAccessTokenInterceptor = object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
if (tokenExpired()) updateAccessToken()
val initialRequest = chain.request()
val initialResponse = chain.proceed(initialRequest)
if (initialResponse.code == HttpURLConnection.HTTP_UNAUTHORIZED && sessionRepository.getRefreshToken() != null) {
updateAccessToken()
//we need to close response to be able to start new request
initialResponse.close()
val authorizedRequest = initialRequest
.newBuilder()
.header(
ScpConstants.Api.HEADER_AUTHORIZATION,
ScpConstants.Api.HEADER_PART_BEARER + tokenResponse.accessToken
)
.build()
return chain.proceed(authorizedRequest)
} else {
return initialResponse
}
}
}
val accessTokenOkHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(accessTokenInterceptor)
.addInterceptor(unauthAccessTokenInterceptor)
.build()
//user api
val userRetrofit = Retrofit.Builder()
.baseUrl(BuildConfig.API_IP + BuildConfig.API_URL + ScpConstants.Api.Endpoint.USER)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.client(accessTokenOkHttpClient)
.build()
fun updateAccessToken() {
val tokenResponse = authApi
.getAccessTokenByRefreshTokenSync(
Credentials.basic(
BuildConfig.CLIENT_ID,
BuildConfig.CLIENT_SECRET
),
ScpConstants.Api.GRANT_TYPE_REFRESH_TOKEN,
sessionRepository.getRefreshToken()!!
)
.execute()
.body()
?: throw NullPointerException("Body is null while get accessToken by refreshToken!")
sessionRepository.setAccessToken(tokenResponse.accessToken)
}