Как я могу обработать пустое тело ответа с помощью Retrofit 2?

125

Недавно я начал использовать Retrofit 2 и столкнулся с проблемой синтаксического анализа пустого тела ответа. У меня есть сервер, который отвечает только HTTP-кодом без какого-либо содержимого внутри тела ответа.

Как я могу обрабатывать только метаинформацию об ответе сервера (заголовки, код состояния и т. Д.)?

Евгений Деркач
источник

Ответы:

216

Редактировать:

Как отмечает Джейк Уортон,

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

это лучший способ пойти против моего первоначального ответа -

Вы можете просто вернуть 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
    }
});
iagreen
источник
58
Еще лучше: используйте, Voidкоторый не только имеет лучшую семантику, но и (немного) более эффективен в пустом случае и намного эффективнее в непустом случае (когда вам просто наплевать на тело).
Джейк Уортон,
1
@JakeWharton: Отличное поведение. Спасибо, что указали на это. Ответ обновлен.
iagreen
2
Отличный ответ. Одна из причин не использовать Void - это если у вас есть ресурс, который возвращает тело только в случае неудачного запроса, и вы хотите преобразовать errorBody ResponseBody в какой-то конкретный или общий тип.
7
@JakeWharton Отличное предложение для использования Void. Будет ли использование Unitкода Kotlin таким же преимуществом, как и использование VoidJava for Retrofit?
Акшай Чордия
6
@ akshay-chordiya Я только что проверил, Unitв Котлине НЕ работает, Voidно работает. Я предполагаю, что где-то есть жестко запрограммированная проверка.
user3363866 04
41

Если вы используете 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 */);
Гэн Цзявэнь
источник
Вот что я нашел! Спасибо!
Sirelon
Я использовал 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");
        }
    });

}
Моти Бартов
источник