问题
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.
回答1:
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)
回答2:
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!
来源:https://stackoverflow.com/questions/50386909/retrofit2-handling-http-204-no-content-response-situation-with-rxjavas-obser