Bagaimana cara menangani badan respons kosong dengan Retrofit 2?


125

Baru-baru ini saya mulai menggunakan Retrofit 2 dan saya menghadapi masalah dengan penguraian badan respons kosong. Saya memiliki server yang hanya merespons dengan kode http tanpa konten apa pun di dalam tubuh respons.

Bagaimana saya bisa menangani hanya informasi meta tentang respons server (header, kode status, dll)?

Jawaban:


216

Edit:

Seperti yang ditunjukkan Jake Wharton,

@GET("/path/to/get")
Call<Void> getMyData(/* your args here */);

adalah cara terbaik untuk membandingkan tanggapan asli saya -

Anda bisa mengembalikan a ResponseBody, yang akan mengabaikan penguraian respons.

@GET("/path/to/get")
Call<ResponseBody> getMyData(/* your args here */);

Kemudian dalam panggilan Anda,

Call<ResponseBody> dataCall = myApi.getMyData();
dataCall.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Response<ResponseBody> response) {
        // use response.code, response.headers, etc.
    }

    @Override
    public void onFailure(Throwable t) {
        // handle failure
    }
});

58
Bahkan lebih baik: Gunakan Voidyang tidak hanya memiliki semantik yang lebih baik tetapi (sedikit) lebih efisien dalam wadah kosong dan jauh lebih efisien dalam wadah tidak kosong (bila Anda tidak peduli dengan tubuh).
Jake Wharton

1
@JakeWhhon Itu adalah perilaku yang bagus. Terima kasih telah menunjukkannya. Jawaban diperbarui.
iagreen

2
Jawaban yang bagus. Salah satu alasan untuk tidak menggunakan Void adalah jika Anda memiliki sumber daya yang hanya mengembalikan isi saat permintaan tidak berhasil dan Anda ingin mengonversi errorBody ResponseBody ke beberapa tipe tertentu atau umum.

7
@JakeWharton Saran bagus untuk digunakan Void. Apakah menggunakan Unitkode Kotlin akan memberikan manfaat yang sama Voiddengan Java untuk Retrofit?
Akshay Chordiya

6
@ akshay-chordiya Saya baru saja memeriksa, Unitdi Kotlin TIDAK berfungsi, Voidnamun. Saya berasumsi ada pemeriksaan hardcode di suatu tempat.
pengguna3363866

41

Jika Anda menggunakan RxJava, maka lebih baik digunakan Completabledalam kasus ini

Merepresentasikan komputasi yang ditangguhkan tanpa nilai apa pun tetapi hanya indikasi untuk penyelesaian atau pengecualian. Kelas mengikuti pola peristiwa serupa sebagai Reactive-Streams: onSubscribe (onError | onComplete)?

http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Completable.html

dalam jawaban yang diterima:

@GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);

Jika titik akhir mengembalikan kode respons kegagalan, kode itu akan tetap ada di onNextdan Anda harus memeriksa kode respons sendiri.

Namun, jika Anda menggunakan Completable.

@GET("/path/to/get")
Completable getMyData(/* your args here */);

Anda hanya akan memiliki onCompletedan onError. jika kode respons berhasil, kode respons akan aktif, yang onCompletelain akan aktif onError.


1
Apa isi onError Throwableargumen itu, dalam kasus itu? Saya menemukan ini lebih bersih, tetapi kita sering masih perlu melihat kode respons dan isi untuk kegagalan.
big_m

24

Jika Anda menggunakan rxjava, gunakan sesuatu seperti:

@GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);

Itulah yang saya temukan! Terima kasih!
Sirelon

Saya telah menggunakan ResposeBody dengan RxJava2 dan Retrofit2 untuk permintaan PUT REST. Ini bekerja dengan baik
Moti Bartov

1
Kami memiliki API titik akhir yang mengembalikan badan kosong saat berhasil, dan badan json saat kesalahan. Jika menggunakan Respon <Void>, bagaimana cara menangani kasus kesalahan?
KeNVin Favo

Kelas Respon mana yang Anda gunakan di sini? Retrofit atau OKHttps?
Matthias

1
Ini bukan pilihan yang baik jika Anda melakukan penanganan kesalahan pada pengecualian .. Anda tidak mendapatkan pengecualian dengan pendekatan ini, melainkan JSON sebagai respons atas kesalahan
Ovi Trif

0

Berikut adalah cara saya menggunakannya dengan Rx2 dan Retrofit2, dengan permintaan PUT REST: Permintaan saya memiliki badan json tetapi hanya kode tanggapan http dengan badan kosong.

Klien Api:

public class ApiClient {
public static final String TAG = ApiClient.class.getSimpleName();


private DevicesEndpoint apiEndpointInterface;

public DevicesEndpoint getApiService() {


    Gson gson = new GsonBuilder()
            .setLenient()
            .create();


    OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    okHttpClientBuilder.addInterceptor(logging);

    OkHttpClient okHttpClient = okHttpClientBuilder.build();

    apiEndpointInterface = new Retrofit.Builder()
            .baseUrl(ApiContract.DEVICES_REST_URL)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
            .create(DevicesEndpoint.class);

    return apiEndpointInterface;

}

Antarmuka:

public interface DevicesEndpoint {
 @Headers("Content-Type: application/json")
 @PUT(ApiContract.DEVICES_ENDPOINT)
 Observable<ResponseBody> sendDeviceDetails(@Body Device device);
}

Kemudian untuk menggunakannya:

    private void sendDeviceId(Device device){

    ApiClient client = new ApiClient();
    DevicesEndpoint apiService = client.getApiService();
    Observable<ResponseBody> call = apiService.sendDeviceDetails(device);

    Log.i(TAG, "sendDeviceId: about to send device ID");
    call.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<ResponseBody>() {
        @Override
        public void onSubscribe(Disposable disposable) {
        }

        @Override
        public void onNext(ResponseBody body) {
            Log.i(TAG, "onNext");
        }

        @Override
        public void onError(Throwable t) {
            Log.e(TAG, "onError: ", t);

        }

        @Override
        public void onComplete() {
            Log.i(TAG, "onCompleted: sent device ID done");
        }
    });

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.