Як я можу обробити порожній орган відповіді за допомогою Retrofit 2?


125

Нещодавно я почав використовувати Retrofit 2 і зіткнувся з проблемою розбору порожнього тіла відповіді. У мене є сервер, який відповідає лише з http-кодом без вмісту всередині органу відповіді.

Як я можу обробляти лише метаінформацію про відповідь сервера (заголовки, код статусу тощо)?

Відповіді:


216

Редагувати:

Як вказує Джейк Уортон,

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

це найкращий шлях до моєї оригінальної відповіді -

Ви можете просто повернути a ResponseBody, що обійде розбір відповіді.

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

Тоді у своєму дзвінку,

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
Ще краще: використовуйте те, Voidщо не тільки має кращу семантику, але є (трохи) ефективнішим у порожньому випадку та значно ефективнішим у непустому випадку (коли ви просто не піклуєтесь про тіло).
Джейк Уортон

1
@JakeWharton Це чудова поведінка. Дякуємо, що вказали на це. Відповідь оновлено.
iagreen

2
Чудова відповідь. Однією з причин не використовувати Void є те, що у вас є ресурс, який повертає тіло лише тоді, коли запит не виконаний, і ви хочете перетворити errorBody ResponseBody в якийсь конкретний або поширений тип.

7
@JakeWharton Чудова пропозиція використовувати Void. Чи використання Unitкоду Котліна дасть ту саму перевагу, що і Voidв Java для Retrofit?
Акшай Чордія

6
@ akshay-chordiya Я щойно перевірив, Unitу Котліні НЕ працює, Voidпроте. Я припускаю, що десь є жорсткий код перевірки.
користувач3363866

40

Якщо ви використовуєте RxJava, то краще використовувати Completableв цьому випадку

Представляє відкладені обчислення без будь-якого значення, але лише вказівку на завершення або виняток. Клас дотримується аналогічного шаблону подій, як і Reactive-Streams: onSubscribe (onError | onComplete)?

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

у прийнятій відповіді:

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

Якщо кінцева точка повертає код відповіді на відмову, він все ще буде знаходитись у, onNextі вам доведеться перевірити код відповіді самостійно.

Однак якщо ви користуєтесь Completable.

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

у вас буде тільки onCompleteі onError. якщо код відповіді буде успішним, він запустить onCompleteінше, він запустить onError.


1
Що буде onError Throwableмістити аргумент у такому випадку? Я вважаю це чистішим, але нам часто все-таки потрібно переглянути код відповіді та тіло на предмет відмов.
big_m

24

Якщо ви використовуєте rxjava, використовуйте щось на кшталт:

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

Ось що я знайшов! Дякую!
Сирелон

Я використовував ResposeBody з RxJava2 та Retrofit2 для запиту PUT REST. Це спрацювало чудово
Моті Бартов

1
У нас є API кінцевої точки, який повертає порожнє тіло при успіху, і тіло json при помилці. Якщо я використовую Response <Void>, як я можу обробити випадок помилки?
KeNVin Favo

Який клас відповідей ви тут використовуєте? Модернізація чи OKHttps?
Маттіас

1
Це не гарний варіант, якщо ви працюєте з помилками за винятками .. при такому підході ви не виняток, а скоріше JSON як відповідь на помилку
Ovi Trif

0

Ось як я використовував це з Rx2 та Retrofit2, із запитом PUT REST: У моєму запиті було тіло json, але просто http відповіді з порожнім тілом.

Клієнт 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;

}

Інтерфейс:

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

Тоді для його використання:

    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");
        }
    });

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.