Patrick Brennan
Patrick Brennan

Reputation: 2738

Kotlin and Retrofit : How to Handle HTTP 400 responses?

I am using Retrofit (2.6) on Android to implement a service which connects to a web server, and which requests that the server undertake some work. The relevant code can be summarized thus:

interface MyService {
    @GET(START_WORK)
    suspend fun startWork(@Query("uuid") uuid: String,
                          @Query("mode") mode: Int):
        MyStartWorkResponse
}

// Do some other things, include get a reference to a properly configured
// instance of Retrofit.

// Instantiate service
var service: MyService = retrofit.create(MyService::class.java)

I can call service.startWork() with no problem and obtain valid results. However, in some conditions, the web server will return a 400 error code, with a response body which includes specific error information. The request is not malformed, however; it's just that there is another problem which should be brought to the user's attention. The trouble is, I can't tell what the problem is, because I don't get a response; instead, my call throws an exception because of the 400 error code.

I don't understand how to modify my code so that I can catch and handle 400 error responses, and get the information I need from the body of the response. Is this a job for a network interceptor on my okhttp client? Can anyone shed some light?

Upvotes: 2

Views: 6660

Answers (2)

Bacar Pereira
Bacar Pereira

Reputation: 1145

Use this code (KOTLIN)

class ApiClient {

    companion object {

        private val BASE_URL = "YOUR_URL_SERVER"
        private var retrofit: Retrofit? = null

        private val okHttpClientvalor = OkHttpClient.Builder()
            .connectTimeout(90, TimeUnit.SECONDS)
            .writeTimeout(90, TimeUnit.SECONDS)
            .readTimeout(90, TimeUnit.SECONDS)
            .build()

        fun apiClient(): Retrofit {
            if (retrofit == null) {
                retrofit = Retrofit.Builder().baseUrl(BASE_URL)
                    .client(okHttpClientvalor)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
            }
            return retrofit!!
        }

    }

}


object ErrorUtils {

    fun parseError(response: Response<*>): ErrorResponce? {
        val conversorDeErro = ApiClient.apiClient()
            .responseBodyConverter<ErrorResponce>(ErrorResponce::class.java, arrayOfNulls(0))
        var errorResponce: ErrorResponce? = null
        try {
            if (response.errorBody() != null) {
                errorResponce = conversorDeErro.convert(response.errorBody()!!)
            }
        } catch (e: IOException) {
            return ErrorResponce()
        } finally {
            return errorResponce
        }
    }
}


class ErrorResponce {
     /* This name "error" must match the message key returned by the server.
  Example: {"error": "Bad Request ....."} */
    @SerializedName("error")
    @Expose
    var error: String? = null
}

    if (response.isSuccessful) {
        return MyResponse(response.body()  // transform
                ?:  // some empty object)
    } else {
        val errorResponce = ErrorUtils.parseError(response)
        errorResponce!!.error?.let { message ->
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
    }
}

Upvotes: 1

Dmitri
Dmitri

Reputation: 2972

Retrofit defines successful response as such:

public boolean isSuccessful() { return code >= 200 && code < 300; }

which means you should be able to do something like this

class ServiceImpl(private val myService: MyService) {
   suspend fun startWork(//query): MyResponse =

    myService.startWork(query).await().let {

    if (response.isSuccessful) {
        return MyResponse(response.body()//transform
                ?: //some empty object)
    } else {
        throw HttpException(response)//or handle - whatever
    }
}

}

Upvotes: 0

Related Questions