I have a web-service which return a list of users for a topic, if there is no any user for that topic it just returns HTTP code 204( No Content).
This is my retrofit2 call for that service (in Kotlin)
@GET("/user/{topic}") fun getAllUserFor(@Path(value="topic",encoded=true) topic:String) :Observable<List<User>>
And my execution is :
fun getAllUsers(topic: String, onSuccess: Consumer<List<User>>, onFail:Consumer<Throwable>){
val api = NetworkLayer.getUserApi()
api.getAllUserFor(topic)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(onSuccess,onFail)
}
Works fine except when there is no users for the topic, because when there is no users server respond with code 204 which received as an exception.
I'm using ScalarsConverterFactory & GsonConverterFactory to parse the Json response with Retrofit2.
similar issue discussed here but their server responds only with http code without any content! In my case server will return a user list as a Json body when there is one or more users & response with 204 when there is no users, So I need to handle both cases along with other usual http errors.
Standard way is wrapping the response in a Result
that comes with adapter-rxjava2 dependency.
@GET("/user/{topic}")
fun getAllUserFor(@Path(value="topic",encoded=true) topic:String) :Observable<Result<List<User>>>
this way errors too are delivered to your onNext and you can do something like this:
api.getAllUserFor(topic)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe{ result ->
if(result.isError)
//Network Error
result.response()?.also {
if(result.isSuccessful)
//Success
else{
//api error
}
}
}
much cleaner and more concise
check for status with
result.response?.code()
cheers!
You can achieve that by implementing an operator that will check whether the status code of the received response is 204:
class OnApiErrorOperator : ObservableOperator<OnApiErrorOperator.User, Response<OnApiErrorOperator.User>> {
override fun apply(observer: Observer<in OnApiErrorOperator.User>): Observer<in Response<OnApiErrorOperator.User>> {
return object : Observer<Response<OnApiErrorOperator.User>> {
override fun onSubscribe(d: Disposable) {
observer.onSubscribe(d)
}
override fun onNext(response: Response<OnApiErrorOperator.User>) {
if (response.code() == 204) {
//return User object with empty list when response's status code is 204
observer.onNext(User(emptyList()))
} else {
if (response.isSuccessful) {
observer.onNext(response.body()!!)
} else {
//don't forget to check for other statuses
observer.onError(OnApiServerErrorException(response.code()))
}
}
}
override fun onError(e: Throwable) {
observer.onError(e)
}
override fun onComplete() {
observer.onComplete()
}
}
}
class User(
@SerializedName("user_info")
val userInfo: List<String>
)
class OnApiServerErrorException(val statusCode: Int) : Exception()
}
And then you can add operator lift
in your observables chain to call it:
api.getAllUserFor(topic)
.subscribeOn(Schedulers.io())
.lift(OnApiErrorOperator())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onSuccess, onFail)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With