Fuel: How to handle 4XX response

Created on 8 Sep 2017  路  8Comments  路  Source: kittinunf/fuel

Hi, thanks for great library.
I have a question about handling 400 response.

I am developing an Android application on Kotlin and use rx_responseString() method to get a JSON from a Web API.

Api responses a JSON like bellow.

{
    "firstName": "hoge"
    "lastName" : "foo"
}

Data class for response.

data class User(val firstName: String, val lastName: String)

I want to get a User object from a api's response with GSON.

// In a data access class
fun getUser(): Single<User> {

  // send a get request and convert response
  return "http://XXX/user".httpGet().rx_responseString()
    map {
      p ->
      val gson = Gson()
      gson.fromJson(p.second.get(), User::class.java)
    }
}

In an activity, I retrieve s result and display.
When response status code is 4XX, I want to display error dialog with message.

// In an Activity
getUser().subscribeOn(Schedulers.newThread())
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe(
         {
            // onSuccess
            res ->
              // display json to an Activity
         },
         {
            // onError
            e ->
              // check http status code
              when (httpStatusCode) {
                400 -> {
                     // show dialog with message
                }
                404 -> {
                    // show dialog with message
                } 
         }
         )

I want to know best practices to handle response with status code 4XX.

question

Most helpful comment

Thank you for replying!
If I apply clean architecture like https://blog.uptech.team/clean-architecture-in-android-with-kotlin-rxjava-dagger-2-2fdc7441edfc,
I guess it's better to keep the return type of getUser method Single and tried to return an error object in an exception as below.
How is this?

// error object
data class ApiError(val errorCode: String, val message: String)

```kotlin
// the field contains an Api Error object.
class ApiException(val apiError: QiitaError) : Exception()

```kotlin
fun getUser(): Single<User> {

    return url.httpGet().rx_responseString().flatMap { p ->
        when (p.first.httpStatusCode) {
            200 -> {
                val gson = Gson()
                Single.create<User> { e ->
                    e.onSuccess(gson.fromJson(p.second.get(), User::class.java))
                }
            }
            400..419 -> {
                // handle an 4XX error 
                val input = InputStreamReader(ByteArrayInputStream(p.first.data))
                val gson = Gson()
                val error = gson.fromJson(input, ApiError::class.java)
                Single.error(QiitaException(error))
            }
            else -> {}
        }
    }

All 8 comments

you can check statusCode in res object

getUser().subscribeOn(Schedulers.newThread())
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe({ res ->
            val statusCode = res.first.statusCode
            when (statusCode) {
               400 -> { }
               401 -> { }
            }
         }, { e -> })

hope this answer your question :)

I checked that valid HTTP status code is 200 to 299 so 4XX will go to error case. Currently we cannot retrieve status code in error object yet.

@kittinunf Can we override HTTP status code validation?

Yes,

how about this;

val manager = FuelManager()
manager.addResponseInterceptor(validatorResponseInterceptor(400..419))

manager.request(Method.GET, "/status/$preDefinedStatusCode").response { req, res, result ->
    request = req
    response = res

    when (result) {
        is Result.Failure -> {
        }
        is Result.Success -> {
            //it will be validated as Success according to the interceptor.
        }
    }
}

Looks nice. I will try it :D. Thank you so much.

Thank you for replying! And let me ask you one more thins.
Api returns error information JSON as below when an error occur.

{ 
  "errorCode": "parameterError",
  "message": "validation failed"
}

I want to convert this json to a kotlin object.
How should I return an error object from getUser() method?

getUser().subscribeOn(Schedulers.newThread())
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe({ res ->
            val statusCode = res.first.statusCode
            when (statusCode) {
               400 -> { 
                      // handle error object which contains error JSON
               }
               401 -> { }
            }
         }, { e -> })

I assume that you have User class and Error class for success and failure cases.
You might need to deserialize JSON in subscribe instead of getUser().
You could do like this

fun getUser(): Single<User> {
  return "http://XXX/user".httpGet().rx_responseString()
}
getUser().subscribeOn(Schedulers.newThread())
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe({ res ->
            val statusCode = res.first.statusCode

            when (statusCode) {
               200 -> Gson().fromJson(res.second.get(), User::class.java)
               in 400..419 -> Gson().fromJson(res.second.get(), Error::class.java)
               else -> { }
            }
         }, { e -> })

Thank you for replying!
If I apply clean architecture like https://blog.uptech.team/clean-architecture-in-android-with-kotlin-rxjava-dagger-2-2fdc7441edfc,
I guess it's better to keep the return type of getUser method Single and tried to return an error object in an exception as below.
How is this?

// error object
data class ApiError(val errorCode: String, val message: String)

```kotlin
// the field contains an Api Error object.
class ApiException(val apiError: QiitaError) : Exception()

```kotlin
fun getUser(): Single<User> {

    return url.httpGet().rx_responseString().flatMap { p ->
        when (p.first.httpStatusCode) {
            200 -> {
                val gson = Gson()
                Single.create<User> { e ->
                    e.onSuccess(gson.fromJson(p.second.get(), User::class.java))
                }
            }
            400..419 -> {
                // handle an 4XX error 
                val input = InputStreamReader(ByteArrayInputStream(p.first.data))
                val gson = Gson()
                val error = gson.fromJson(input, ApiError::class.java)
                Single.error(QiitaException(error))
            }
            else -> {}
        }
    }

Looks nice. 馃槃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

iNoles picture iNoles  路  5Comments

NikkyAI picture NikkyAI  路  3Comments

lfmingo picture lfmingo  路  4Comments

jsyeo picture jsyeo  路  5Comments

Xianyang picture Xianyang  路  4Comments