Я пытаюсь вручную обработать логику токена обновления в Ktor, и она работает нормально, но проблема, с которой я столкнулся, заключается в том, что как только я получу ответ от API токена обновления, как повторно запросить неудачный API? Он дважды выполняет неудачный API, прежде чем нажать API обновления токена. Не знаю почему. Я попробовал приведенный ниже код, но он вызывает цикл продолжения.
Ниже приведена функция, используемая для вызова API
Код: Выделить всё
@Throws(AppException::class)
suspend inline fun get(
block: HttpRequestBuilder.() -> Unit = {}
): T = try {
request(block)
} catch (e: AppException) {
throw e
}
Код: Выделить всё
@PublishedApi
internal suspend inline fun request(
block: HttpRequestBuilder.() -> Unit
): T = defaultHttpClient
.request(
HttpRequestBuilder()
.apply {
headersMap().forEach { (key, value) -> header(key, value) }
authToken().accessToken?.let { header(AUTHORIZATION, it) }
}.apply(block)
).body()
Код: Выделить всё
private fun httpClient() = HttpClient(engine = configuration.engine()) {
configuration.requestRetry?.let { config ->
val configuration: HttpRequestRetryConfig = HttpRequestRetryConfig().apply(config)
install(HttpRequestRetry) {
configuration.maxRetry?.let { maxRetries ->
val max = maxRetries.invoke()
/**
retryOnException: It will retry on all exception except cancellation exception
*/
retryOnException(
maxRetries = max,
retryOnTimeout = configuration.retryOnTimeout.invoke()
)
/**
retry if status is not success: it will retry is status code is not [200, 300]
*/
retryIf(maxRetries = max) { _, response ->
!response.status.isSuccess()
}
}
exponentialDelay()
modifyRequest {
request.headers.append("x-retry-count", retryCount.toString())
}
}
}
install(ContentNegotiation) {
json(
Json {
ignoreUnknownKeys = true
encodeDefaults = true
isLenient = true
prettyPrint = true
explicitNulls = false
},
)
}
Logging {
logger = object : Logger {
override fun log(message: String) {
if (configuration.logging) {
httpMetrics.log(message)
}
}
}
level = LogLevel.ALL
}
install(HttpTimeout) {
requestTimeoutMillis = timeoutConfig.requestTimeoutMillis
connectTimeoutMillis = timeoutConfig.connectTimeoutMillis
socketTimeoutMillis = timeoutConfig.socketTimeoutMillis
}
defaultRequest {
url {
protocol = URLProtocol.createOrDefault(environment.protocol)
host = environment.baseUrl
}
contentType(configuration.contentType)
}
HttpResponseValidator {
validateResponse { response ->
val statusCode = response.status
val originCall = response.call
if (statusCode.value < 300 || originCall.attributes.contains(ValidateMark)) return@validateResponse
val exceptionCall = originCall.save().apply {
attributes.put(ValidateMark, Unit)
}
when (statusCode) {
HttpStatusCode.NoContent -> {
throw AppException(
ErrorResponse(
HttpStatusCode.NoContent.value.toString(),
HttpStatusCode.NoContent.description
)
)
}
HttpStatusCode.BadRequest -> {
if (response.request.url.fullPath.contains("refreshToken")) {
// logout user if in refreshToken api we get 400
userManager.logout()
return@validateResponse
}
}
HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden -> {
if (response.request.url.fullPath.contains("refreshToken")) {
// logout user if in refreshToken api we get 401 or 403
userManager.logout()
return@validateResponse
} else {
val refreshTokenResponse: RefreshTokenModel = request(
RefreshToken(
refreshToken = authToken().refreshToken.orEmpty(),
email = authToken().workEmail.orEmpty(),
deviceId = deviceId.orEmpty()
)
)
// Once user get the valid response form refresh token api
// then again hit the failed API.
val req: HttpRequestBuilder.() -> Unit = {
url {
path(originCall.request.url.fullPath)
}
// headers[HttpHeaders.Authorization] = "Bearer ${refreshTokenResponse.token}"
refreshTokenResponse.token?.let { bearerAuth(it) }
setBody(originCall.request.content)
method = originCall.request.method
}
originCall.client.request(HttpRequestBuilder().apply(req))
return@validateResponse
}
}
}
val exceptionResponse = exceptionCall.response
val exceptionResponseText = try {
exceptionResponse.bodyAsText()
} catch (_: MalformedInputException) {
BODY_FAILED_DECODING
}
val errorResponse = Json.decodeFromString(exceptionResponseText)
throw AppException(errorResponse)
}
}
}
- Если мы нажимаем на запрос API, и он выдает 401 или 403 нам нужно обновить токен.
- Как только мы получим успешный ответ от API обновления токена.
- Мы снова продолжаем работать с неудачным API.
Код: Выделить всё
originCall.client.request(HttpRequestBuilder().apply(req))Подробнее здесь: https://stackoverflow.com/questions/780 ... ot-working
Мобильная версия