Authentication aufräumen, 3. Schritt Error Handling f. auth + HttpClient
This commit is contained in:
1 parent
9b573243ef
commit
e8bd21d108
4 files changed
+107
-16
No files matched your search
@@ -2,12 +2,17 @@ package net.keinekekse.app.service
|
||||
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.auth.*
|
||||
import io.ktor.client.plugins.auth.providers.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.util.network.*
|
||||
import kotlinx.io.IOException
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.keinekekse.app.auth.Authenticator
|
||||
import org.koin.core.annotation.Single
|
||||
@@ -18,6 +23,7 @@ private val logger = KotlinLogging.logger {}
|
||||
@Single
|
||||
fun createHttpClient(authenticator: Authenticator): HttpClient {
|
||||
val client = HttpClient {
|
||||
expectSuccess = true
|
||||
install(Auth) {
|
||||
bearer {
|
||||
loadTokens {
|
||||
@@ -36,7 +42,6 @@ fun createHttpClient(authenticator: Authenticator): HttpClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
prettyPrint = true
|
||||
@@ -50,6 +55,7 @@ fun createHttpClient(authenticator: Authenticator): HttpClient {
|
||||
requestTimeoutMillis = 10000
|
||||
connectTimeoutMillis = 5000
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
client.plugin(HttpSend).intercept { request ->
|
||||
@@ -67,3 +73,54 @@ fun createHttpClient(authenticator: Authenticator): HttpClient {
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// HTTP-Error-Handling -> https://stackoverflow.com/questions/54679592/handling-exception-in-httpclient-ktor
|
||||
|
||||
sealed class ApiResponse<out T, out E> {
|
||||
/**
|
||||
* Represents successful network responses (2xx).
|
||||
*/
|
||||
data class Success<T>(val body: T) : ApiResponse<T, Nothing>()
|
||||
|
||||
sealed class ErrorResponse<E> : ApiResponse<Nothing, E>() {
|
||||
/**
|
||||
* Represents server (50x) and client (40x) errors.
|
||||
*/
|
||||
data class HttpError<E>(val code: Int, val errorBody: E?) : ErrorResponse<E>()
|
||||
|
||||
/**
|
||||
* Represent IOExceptions and connectivity issues.
|
||||
*/
|
||||
data class NetworkError(val exception: String) : ErrorResponse<Nothing>()
|
||||
|
||||
/**
|
||||
* Represent SerializationExceptions.
|
||||
*/
|
||||
object SerializationError : ErrorResponse<Nothing>()
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun <reified T, reified E> HttpClient.safeRequest(
|
||||
block: HttpRequestBuilder.() -> Unit,
|
||||
): ApiResponse<T, E> =
|
||||
try {
|
||||
val response = request { block() }
|
||||
ApiResponse.Success(response.body())
|
||||
} catch (e: ClientRequestException) {
|
||||
ApiResponse.ErrorResponse.HttpError(e.response.status.value, e.errorBody())
|
||||
} catch (e: ServerResponseException) {
|
||||
ApiResponse.ErrorResponse.HttpError(e.response.status.value, e.errorBody())
|
||||
} catch (e: IOException) {
|
||||
ApiResponse.ErrorResponse.NetworkError(e.toString())
|
||||
} catch (e: UnresolvedAddressException) { // no Network
|
||||
ApiResponse.ErrorResponse.NetworkError(e.toString())
|
||||
} catch (_: SerializationException) {
|
||||
ApiResponse.ErrorResponse.SerializationError
|
||||
}
|
||||
|
||||
suspend inline fun <reified E> ResponseException.errorBody(): E? =
|
||||
try {
|
||||
response.body()
|
||||
} catch (_: SerializationException) {
|
||||
null
|
||||
}
|
||||
Reference in new issue
Block a user