Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle 204 response in Retrofit using Kotlin Coroutines?

I'm using Retrofit 2.7.1 with Kotlin coroutines.

I have a Retrofit service defined as such:

@PUT("/users/{userId}.json")
suspend fun updateUserProfile(
        @Path("userId") userId: String,
        @Query("display_name") displayName: String) : Void

This call returns an HTTP 204 No Content response, which causes a crash in Retrofit:

kotlin.KotlinNullPointerException: Response from com.philsoft.test.api.UserApiService.updateUserProfile was null but response body type was declared as non-null
        at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:43)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:129)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)

How do I handle a 204 response in retrofit, using coroutines, without crashing?

like image 415
PhillyTheThrilly Avatar asked Jan 07 '20 21:01

PhillyTheThrilly


3 Answers

According to this, use Response<Unit> in the retrofit method declaration.

retrofit no content issue

like image 160
Dmitri Avatar answered Oct 17 '22 21:10

Dmitri


You can use Response<Unit> to handle 204 responses using Retrofit but when you handle like this Retrofit will not throw an exception for 4xx responses or other exceptional cases.

You may deal with these cases with the following code:

return try {
        val result = apiCall.invoke()
        return if (result is Response<*>) {
            if (result.code() >= 400) {
                // create an error from error body and return
            } else {
                // return success result
            }
        } else {
            // directly return success result
        }
    } catch (t: Throwable) {
        // create an error from throwable and return it!
    }
like image 30
savepopulation Avatar answered Oct 17 '22 19:10

savepopulation


Explaining answer of @Dmitri with the sample code:

  1. In your APIInterface, call your API like this,

     @GET(AppConstants.APIEndPoints.GET_NEWS)
     suspend fun getNews(
         @Query("limit") limit: String,
     ): Response<NewsListResponseModel>
    

where, Response is retrofit2.Response

  1. From where you call the API, check for the status code from the function like apiResponse.code().

    val apiResponse = apiEndPointsInterface.getNews(limit)
    if (apiResponse.code() == HttpURLConnection.HTTP_OK) {
         //response success
         ResultOf.Success(apiResponse.body()!!))
     } else {
         //handle your other response codes here
         ResultOf.Failure("No Data Found.", null))
     }
    
like image 8
Kishan Solanki Avatar answered Oct 17 '22 19:10

Kishan Solanki