Dapatkan kode status respons menggunakan Retrofit 2.0 dan RxJava

108

Saya mencoba meningkatkan ke Retrofit 2.0 dan menambahkan RxJava di proyek android saya. Saya melakukan panggilan api dan ingin mengambil kode kesalahan jika ada respons kesalahan dari server.

Observable<MyResponseObject> apiCall(@Body body);

Dan di panggilan 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
        }
    });

Di Retrofit 1.9, RetrofitError masih ada dan kita bisa mendapatkan statusnya dengan melakukan:

error.getResponse().getStatus()

Bagaimana Anda melakukan ini dengan Retrofit 2.0 menggunakan RxJava?

AB
sumber

Jawaban:

185

Alih-alih mendeklarasikan panggilan API seperti yang Anda lakukan:

Observable<MyResponseObject> apiCall(@Body body);

Anda juga bisa mendeklarasikannya seperti ini:

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

Anda kemudian akan memiliki Pelanggan seperti berikut:

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

Jadi, respons server dengan kode kesalahan juga akan dikirimkan ke onNextdan Anda bisa mendapatkan kode tersebut dengan menelepon reponse.code().

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

EDIT: Oke, akhirnya saya sempat melihat apa yang dikatakan e-nouri dalam komentar mereka, yaitu hanya kode 2xx yang mau onNext. Ternyata kami berdua benar:

Jika panggilan dideklarasikan seperti ini:

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

atau bahkan ini

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

semua tanggapan akan berakhir onNext, terlepas dari kode kesalahannya. Ini dimungkinkan karena semuanya dibungkus dalam sebuah Responseobjek oleh Retrofit.

Sebaliknya, jika panggilan tersebut dinyatakan seperti ini:

Observable<MyResponseObject> apiCall(@Body body);

atau ini

Observable<ResponseBody> apiCall(@Body body);

memang hanya tanggapan 2xx yang akan diberikan onNext. Segala sesuatu yang lain akan dibungkus HttpExceptiondan dikirim ke onError. Yang juga masuk akal, karena tanpa Responsepembungkusnya, apa yang harus dipancarkan onNext? Mengingat bahwa permintaan itu tidak berhasil, satu-satunya hal yang masuk akal untuk dikeluarkan adalah null...

david.mihola
sumber
16
Bagi orang yang mencari kode HTTP 4xx, mereka akan berakhir sebagai HttpException di onError. onNext hanya akan memiliki 2xx.
e-nouri
2
Itu menarik ... Saya baru saja memeriksa lagi ( gist.github.com/DavidMihola/17a6ea373b9312fb723b ) dan semua kode yang saya coba akhirnya onNexttermasuk 404, dll. Saya menggunakan Retrofit v2.0.0-beta3.
david.mihola
@ e-nouri: Saya baru saja menambahkan satu paragraf pada jawaban saya yang mempertimbangkan komentar Anda!
david.mihola
1
@ david.mihola Jika Membangun Retrofit melalui add GsonConverterFactory, sama seperti Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()), Bagaimana cara mendapatkan respon aslinya?
Honghe.Wu
1
Selain penanganan kesalahan, saya pikir Anda mungkin ingin dapat memeriksa header respons apa pun yang disertakan dengan tubuh - itu juga hanya mungkin jika Anda mendapatkan Response. Saya jarang menggunakannya juga - tetapi saya pikir ada baiknya mengetahui bahwa itu ada.
david.mihola
112

Di dalam metode onError, masukkan ini untuk mendapatkan kode

((HttpException) e).code()
Diveno
sumber
7
Ini mungkin salah satu jawaban paling diremehkan yang pernah saya lihat di SO. Ini jenius. Anda dapat melakukan semua yang mungkin perlu Anda lakukan dengan tanggapan tersebut HttpException. Saya menggunakan RxJava dan saya perlu mengurai respons setelah menerima file HTTP 400 BAD REQUEST. Terima kasih banyak!
GabrielOshiro
29
Pastikan untuk memeriksa apakah ini adalah turunan dari HttpException sebelumnya karena NetworkErrors akan menjadi IOExceptions dan tidak memiliki kode status.
Benjamin Mesing
Untuk mengikuti dari @BenjaminMesing mengatakan tentang NetworkError, jika Anda melakukan lebih banyak operator hilir (map / flatMap / forEach dll ..) di Rx Anda sebelum Anda berlangganan maka mungkin ada sejumlah kemungkinan jenis pengecualian, dan belum tentu kesalahan pada permintaan jaringan.
Mark Keen
java.lang.ClassCastException: java.lang.Throwable tidak dapat dilemparkan ke retrofit2.HttpException
Someone Somewhere
7

Anda harus mencatat bahwa pada Retrofit2 semua respons dengan kode 2xx akan dipanggil dari callback onNext () dan kode HTTP lainnya seperti 4xx, 5xx akan dipanggil pada callback onError () , menggunakan Kotlin saya telah menemukan sesuatu seperti ini di 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))
  }
MohammadReza
sumber