Reputation: 9394
I am using the following method to handle my requests
override fun intercept(chain: Interceptor.Chain): Response = chain.proceed(chain.request())
.let { originalResponse ->
Log.i("AMIRA999", "code : " + originalResponse.code())
when (originalResponse.code()) {
200 -> {
Log.i("AMIRA999", "body : " + getErrorResponse(originalResponse))
originalResponse
}
401, 404 -> {
Log.i("AMIRA999", "body : " + getErrorResponse(originalResponse))
originalResponse
/*return originalResponse.mapToBody(
originalResponse.body()?.contentType(),
getErrorResponse(originalResponse)
)*/
}
else -> {
Log.i("AMIRA999", "body : " + originalResponse.body().toString())
throw BadRequestException()
}
}
}
the method work perfect when the code is 200, but it crash if the code is 404 or 401
what I need to keep returning the json comes from server and does not crash to be able to handle it with error message
how can I do that ?
the crash that I got is the following
retrofit2.HttpException: HTTP 401 UNAUTHORIZED
at com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory$BodyCallAdapter$adapt$2.onResponse(CoroutineCallAdapterFactory.kt:104)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:129)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:206)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Upvotes: 2
Views: 3481
Reputation: 4902
You use retrofit2-kotlin-coroutines-adapter and the exception throwing is by design. Any non-2xx
HTTP response such as 401
will throw an exception. You can see this for yourself in the library source code
if (response.isSuccessful) {
deferred.complete(response.body()!!)
} else {
deferred.completeExceptionally(HttpException(response))
}
But this is not a problem. You can still access the response and your JSON by doing catch (e: HttpException)
and then calling val yourJson = e.response()?.body() as? YourJson
.
Note that retrofit2-kotlin-coroutines-adapter is deprecated and that you should migrate to Retrofit 2.6.0 or newer. Then you can prefix your Retrofit interface functions with suspend
so you can write nice idiomatic Kotlin code.
Upvotes: 2
Reputation: 3930
Retrofit 2
has a different concept of handling "successful"
requests than Retrofit 1
. In Retrofit 2
, all requests
that can be executed (sent to the API) and for which you’re receiving a response
are seen as "successful"
. That means, for these requests
the onResponse
callback is fired and you need to manually check whether the request
is actually successful (status 200-299)
or erroneous (status 400-599)
.
If the request finished successfully
, we can use the response object
and do whatever we wanted. In case the error
actually failed (remember, status 400-599)
, we want to show the user appropriate information about the issue.
Example
Error Object
Let’s assume your API sends a JSON error body
like this:
{
statusCode: 409,
message: "Email address already registered"
}
Note: you can see your JSON error body
by printing response.errorBody()
To avoid these bad user experiences, we’re mapping the response body to a Java object, represented by the following class.
class APIError {
private val statusCode:Int = 0
private val message:String
fun status():Int {
return statusCode
}
fun message():String {
return message
}
}
Error Handler
object ErrorUtils {
fun parseError(response:Response<*>):APIError {
val converter = ServiceGenerator.retrofit()
.responseBodyConverter(APIError::class.java, arrayOfNulls<Annotation>(0))
val error:APIError
try
{
error = converter.convert(response.errorBody())
}
catch (e:IOException) {
return APIError()
}
return error
}
}
Error Handler in Action
Now you can handle error
in API response
using ErrorUtils
like the following.
val call = service.me()
call.enqueue(object:Callback<User>() {
fun onResponse(call:Call<User>, response:Response<User>) {
if (response.isSuccessful())
{
// use response data and do some fancy stuff :)
}
else
{
// parse the response body …
val error = ErrorUtils.parseError(response)
// … and use it to show error information
// … or just log the issue like we’re doing :)
Log.d("error message", error.message())
}
}
fun onFailure(call:Call<User>, t:Throwable) {
// there is more than just a failing request (like: no internet connection)
}
})
The complete example with a video is here retrofit-2-error-handling.
Upvotes: 0