Nhận mã trạng thái phản hồi bằng Retrofit 2.0 và RxJava


107

Tôi đang cố gắng nâng cấp lên Retrofit 2.0 và thêm RxJava trong dự án Android của mình. Tôi đang thực hiện cuộc gọi api và muốn truy xuất mã lỗi trong trường hợp phản hồi lỗi từ máy chủ.

Observable<MyResponseObject> apiCall(@Body body);

Và trong lệnh gọi 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
        }
    });

Trong Retrofit 1.9, RetrofitError vẫn tồn tại và chúng ta có thể nhận được trạng thái bằng cách:

error.getResponse().getStatus()

Làm cách nào để bạn thực hiện việc này với Retrofit 2.0 bằng RxJava?

Câu trả lời:


185

Thay vì khai báo lệnh gọi API như bạn đã làm:

Observable<MyResponseObject> apiCall(@Body body);

Bạn cũng có thể khai báo nó như thế này:

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

Sau đó, bạn sẽ có một Người đăng ký như sau:

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!
    }
}

Vì vậy, phản hồi của máy chủ có mã lỗi cũng sẽ được gửi đến onNextvà bạn có thể lấy mã bằng cách gọi reponse.code().

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

CHỈNH SỬA: OK, cuối cùng tôi đã tìm hiểu xem e-nouri đã nói gì trong bình luận của họ, cụ thể là chỉ có mã 2xx onNext. Hóa ra cả hai chúng ta đều đúng:

Nếu cuộc gọi được khai báo như thế này:

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

hoặc thậm chí cái này

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

tất cả các phản hồi sẽ kết thúc onNext, bất kể mã lỗi của chúng là gì. Điều này có thể thực hiện được vì mọi thứ đều được bọc trong một Responseđối tượng bởi Retrofit.

Mặt khác, nếu lệnh gọi được khai báo như thế này:

Observable<MyResponseObject> apiCall(@Body body);

hoặc cái này

Observable<ResponseBody> apiCall(@Body body);

thực sự chỉ có phản hồi 2xx onNext. Mọi thứ khác sẽ được gói trong một HttpExceptionvà gửi đến onError. Điều này cũng có ý nghĩa, bởi vì không có Responsetrình bao bọc, cái gì sẽ được phát ra onNext? Cho rằng yêu cầu không thành công, điều hợp lý duy nhất để phát ra sẽ là null...


16
Đối với những người đang tìm kiếm mã HTTP 4xx, họ sẽ kết thúc là HttpException trong onError. onNext sẽ chỉ có 2xx.
e-nouri

2
Điều đó thật thú vị ... Tôi vừa kiểm tra lại ( gist.github.com/DavidMihola/17a6ea373b9312fb723b ) và tất cả các mã tôi đã thử cuối cùng onNextbao gồm 404, v.v. Tôi đã sử dụng Retrofit v2.0.0-beta3.
david.mihola

@ e-nouri: Tôi vừa thêm một đoạn vào câu trả lời của mình có tính đến nhận xét của bạn!
david.mihola

1
@ david.mihola Nếu Build Retrofit thông qua add GsonConverterFactory, just as Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()), Làm cách nào để nhận được phản hồi ban đầu?
Honghe.Wu

1
Ngoài việc xử lý lỗi, tôi nghĩ bạn có thể muốn kiểm tra bất kỳ tiêu đề phản hồi nào đi kèm với phần thân - điều đó cũng chỉ có thể nếu bạn nhận được Response. Tôi cũng hiếm khi sử dụng nó - nhưng tôi nghĩ thật tốt khi biết rằng nó tồn tại.
david.mihola

112

Bên trong phương thức onError đặt cái này để lấy mã

((HttpException) e).code()

7
Đây có thể là một trong những câu trả lời bị đánh giá thấp nhất mà tôi từng thấy trong SO. Đây là thiên tài. Bạn có thể làm mọi thứ bạn có thể cần làm với phản hồi HttpException. Tôi đang sử dụng RxJava và tôi cần phân tích cú pháp phản hồi sau khi nhận được HTTP 400 BAD REQUEST. Cảm ơn rât nhiều!
GabrielOshiro

29
Chỉ cần đảm bảo kiểm tra xem nó có phải là một phiên bản của HttpException trước hay không vì NetworkErrors sẽ là IOExceptions và không có mã trạng thái.
Benjamin Mesing

Tiếp theo từ @BenjaminMesing đã nói về NetworkError, nếu bạn đang thực hiện nhiều toán tử hạ lưu hơn (map / flatMap / forEach, v.v.) trong Rx của bạn trước khi bạn đăng ký thì có thể có một loạt các loại ngoại lệ và không nhất thiết là lỗi trên yêu cầu mạng.
Mark Keen

java.lang.ClassCastException: java.lang.Throwable không thể chuyển sang trang bị thêm2.HttpException
Ai đó ở đâu đó vào

7

Bạn nên lưu ý rằng kể từ Retrofit2, tất cả các phản hồi có mã 2xx sẽ được gọi từ lệnh gọi lại onNext () và phần còn lại của các mã HTTP như 4xx, 5xx sẽ được gọi trên lệnh gọi lại onError () , bằng cách sử dụng Kotlin, tôi đã nghĩ ra một thứ như this trong 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))
  }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.