Ktor Retry неудачный запрос вручную не работаетAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Ktor Retry неудачный запрос вручную не работает

Сообщение Anonymous »


Я пытаюсь вручную обработать логику токена обновления в 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()
defaultHttpClient находится ниже

Код: Выделить всё

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
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»