I am using ktor for my android app and its working fine now we have a new requirement that we need to handle the responseCode 401 or 403. For example if we hit getUsersAPI() call and it got 401 or 403 then we need to hit refreshToken API call and if refreshToken API call is successful then call failed API again with new tokens else logout user.
This is exactly my usecase but only thing is refreshToken should be called on 401 & 403.
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 {
@Suppress("DEPRECATION")
expectSuccess = [email protected]
validateResponse { response ->
val statusCode = response.status.value
val originCall = response.call
if (statusCode == 204 /*|| statusCode == 201*/) throw AppException(
ErrorResponse(
statusCode.toString(),
NO_DATA_FOUND
)
)
if (statusCode < 300 || originCall.attributes.contains(ValidateMark)) {
return@validateResponse
}
if (statusCode == 401 || statusCode == 403) {
println("HANDLE ERROR HERE FOR REFFRESS------------------------------------>>")
}
val exceptionCall = originCall.save().apply {
attributes.put(ValidateMark, Unit)
}
val exceptionResponse = exceptionCall.response
val exceptionResponseText = try {
exceptionResponse.bodyAsText()
} catch (_: MalformedInputException) {
BODY_FAILED_DECODING
}
print(exceptionResponseText)
val errorResponse = Json.decodeFromString<ErrorResponse>(exceptionResponseText)
throw AppException(errorResponse)
}
}
install(Auth) {
bearer {
sendWithoutRequest { request ->
request.url.host == "www.googleapis.com"
}
loadTokens {
val token = authToken().accessToken.orEmpty()
val refreshToken = authToken().refreshToken.orEmpty()
BearerTokens(accessToken = token, refreshToken = refreshToken)
}
refreshTokens {
val workEmail = authToken().workEmail.orEmpty()
val refreshToken = authToken().refreshToken.orEmpty()
val response = client.post {
markAsRefreshTokenRequest()
this.apply(
RefreshToken(
refreshTokenId = "",
refreshToken = refreshToken,
email = workEmail,
deviceId = deviceId!!
)
)
}
if (response.status == HttpStatusCode.Unauthorized || response.status == HttpStatusCode.Forbidden) {
println("application will logout")
null
} else {
println("application in else part")
val ktorLoginResponse = response.body<RefreshTokenModel>()
ktorLoginResponse.data?.let { ktorAccessToken ->
ktorAccessToken.token?.let { accessToken ->
ktorAccessToken.refreshToken?.let { refreshToken ->
BearerTokens(accessToken, refreshToken)
}
}
}
}
}
}
}
}
How can I get this working. PLease suggest me what I am doing wrong