使用Retrofit 2.0和RxJava获取响应状态代码


107

我正在尝试升级到Retrofit 2.0,并在我的android项目中添加RxJava。我正在进行api调用,并希望在服务器出现错误响应的情况下检索错误代码。

Observable<MyResponseObject> apiCall(@Body body);

在RxJava调用中:

myRetrofitObject.apiCall(body).subscribe(new Subscriber<MyResponseObject>() {
        @Override
        public void onCompleted() {

        }

        @Override
        public void onError(Throwable e) {

        }

        @Override
        public void onNext(MyResponseObject myResponseObject) {
           //On response from server
        }
    });

在Retrofit 1.9中,RetrofitError仍然存在,我们可以通过执行以下操作获取状态:

error.getResponse().getStatus()

如何使用RxJava使用Retrofit 2.0?

Answers:


185

不必像声明那样声明API调用:

Observable<MyResponseObject> apiCall(@Body body);

您也可以这样声明:

Observable<Response<MyResponseObject>> apiCall(@Body body);

然后,您将拥有一个类似于以下内容的订户:

new Subscriber<Response<StartupResponse>>() {
    @Override
    public void onCompleted() {}

    @Override
    public void onError(Throwable e) {
        Timber.e(e, "onError: %", e.toString());

        // network errors, e. g. UnknownHostException, will end up here
    }

    @Override
    public void onNext(Response<StartupResponse> startupResponseResponse) {
        Timber.d("onNext: %s", startupResponseResponse.code());

        // HTTP errors, e. g. 404, will end up here!
    }
}

因此,带有错误代码的服务器响应也将传递到onNext,您可以通过调用来获取代码reponse.code()

http://square.github.io/retrofit/2.x/retrofit/retrofit/Response.html

编辑:好的,我终于了解了e-nouri在他们的评论中所说的内容,即只有2xx代码可以到达onNext。原来我们都是对的:

如果调用是这样声明的:

Observable<Response<MyResponseObject>> apiCall(@Body body);

甚至这个

Observable<Response<ResponseBody>> apiCall(@Body body);

onNext不管错误代码是什么,所有响应都将以结尾。这是可能的,因为ResponseRetrofit将所有内容包装在一个对象中。

另一方面,如果调用是这样声明的:

Observable<MyResponseObject> apiCall(@Body body);

或这个

Observable<ResponseBody> apiCall(@Body body);

实际上,只有2xx响应会转到onNext。其他所有内容都将包裹在中HttpException并发送到onError。这也是有道理的,因为如果没有Response包装,应该onNext什么发出?鉴于请求未成功,唯一明智的选择是null...


16
对于寻找HTTP代码4xx的人,他们将在onError中最终成为HttpException。onNext仅具有2xx。
e-nouri

2
有趣的是……我再次检查了一下(gist.github.com/DavidMihola/17a6ea373b9312fb723b),我尝试过的所有代码最终都onNext包含404等。我使用了Retrofit v2.0.0-beta3。
david.mihola,2016年

@ e-nouri:我刚刚在回答中添加了一个段落,考虑了您的评论!
david.mihola,2016年

1
@ david.mihola如果通过add进行改造GsonConverterFactory,就像一样Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()),如何获得原始响应?
Honghe.Wu

1
除了错误处理之外,我认为您可能希望能够检查与正文一起出现的所有响应标头-只有在获得时,这也是可能的Response。我也很少使用它-但我认为知道它确实存在很高兴。
david.mihola

112

在onError方法内部,将其获取代码

((HttpException) e).code()

7
这可能是我在SO中见过的最被低估的答案之一。这是天才。您可以使用进行响应所需的所有操作HttpException。我正在使用RxJava,并且在收到之后需要解析响应HTTP 400 BAD REQUEST。非常感谢你!
加布里埃尔·奥斯朗'16

29
只需确保之前检查它是否为HttpException实例即可,因为NetworkErrors将是IOExceptions并且没有状态码。
本杰明·梅辛

继续@BenjaminMesing所说的有关NetworkError的问题,如果您在订阅前在Rx中进行更多的下游运算符(map / flatMap / forEach等),那么可能会有很多可能的异常类型,并且不一定是错误网络请求。
Mark Keen

java.lang.ClassCastException:java.lang.Throwable中不能转换到retrofit2.HttpException
有人的地方

7

您应该注意,从Retrofit2开始,所有带有代码2xx的响应都将从onNext()回调中调用,其余的HTTP代码(如4xx,5xx)将在onError()回调中调用,使用Kotlin,我想出了类似在onError()中

mViewReference?.get()?.onMediaFetchFinished(downloadArg)
  if (it is HttpException) {
    val errorCode = it.code()
    mViewReference?.get()?.onMediaFetchFailed(downloadArg,when(errorCode){
      HttpURLConnection.HTTP_NOT_FOUND -> R.string.check_is_private
      else -> ErrorHandler.parseError(it)
    })
  } else {
    mViewReference?.get()?.onMediaFetchFailed(downloadArg, ErrorHandler.parseError(it))
  }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.