Come posso gestire un corpo di risposta vuoto con Retrofit 2?


125

Recentemente ho iniziato a utilizzare Retrofit 2 e ho riscontrato un problema con l'analisi del corpo di risposta vuoto. Ho un server che risponde solo con codice http senza alcun contenuto all'interno del corpo della risposta.

Come posso gestire solo le meta informazioni sulla risposta del server (intestazioni, codice di stato, ecc.)?

Risposte:


216

Modificare:

Come sottolinea Jake Wharton,

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

è il modo migliore per andare contro la mia risposta originale -

Puoi semplicemente restituire un ResponseBody, che ignorerà l'analisi della risposta.

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

Quindi nella tua chiamata,

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
Ancora meglio: Usa Voidche non solo ha una semantica migliore, ma è (leggermente) più efficiente nel caso vuoto e molto più efficiente in un caso non vuoto (quando semplicemente non ti importa del corpo).
Jake Wharton

1
@ JakeWharton Questo è un ottimo comportamento. Grazie per averlo fatto notare. Risposta aggiornata.
iagreen

2
Bella risposta. Un motivo per non utilizzare Void è se si dispone di una risorsa che restituisce un corpo solo quando la richiesta non ha esito positivo e si desidera convertire errorBody ResponseBody in un tipo specifico o comune.

7
@ JakeWharton Ottimo suggerimento da usare Void. L'utilizzo Unitdel codice Kotlin darebbe lo stesso vantaggio di VoidJava per Retrofit?
Akshay Chordiya

6
@ akshay-chordiya Ho appena controllato, Unitin Kotlin NON funziona, Voidcomunque. Presumo che da qualche parte ci sia un controllo hardcoded.
user3363866

41

Se usi RxJava, allora è meglio usare Completablein questo caso

Rappresenta un calcolo differito senza alcun valore ma solo un'indicazione per il completamento o l'eccezione. La classe segue un pattern di eventi simile a Reactive-Streams: onSubscribe (onError | onComplete)?

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

nella risposta accettata:

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

Se l'endpoint restituisce un codice di risposta non riuscito, sarà ancora in onNexte sarà necessario controllare il codice di risposta da soli.

Tuttavia, se usi Completable.

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

avrai solo onCompletee onError. se il codice di risposta ha successo, verrà attivato, onCompletealtrimenti verrà attivato onError.


1
Cosa conterrà l' onError Throwableargomento, in quel caso? Lo trovo più pulito, ma spesso dobbiamo ancora esaminare il codice di risposta e il corpo per gli errori.
big_m

24

Se stai usando rxjava, usa qualcosa come:

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

Questo è quello che stavo trovando! Grazie!
Sirelon

Ho usato ResposeBody con RxJava2 e Retrofit2 per la richiesta PUT REST. Ha funzionato bene
Moti Bartov

1
Abbiamo un'API endpoint che restituisce un corpo vuoto in caso di successo e un corpo json in caso di errore. Se si utilizza la risposta <Void>, come posso gestire il caso di errore?
KeNVin Favo

Quale classe di risposta usi qui? Retrofit o OKHttps?
Matthias

1
Non è una buona opzione se
gestisci gli

0

Ecco come l'ho usato con Rx2 e Retrofit2, con richiesta PUT REST: La mia richiesta aveva un corpo json ma solo un codice di risposta http con corpo vuoto.

Il client 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;

}

L'interfaccia:

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

Quindi per usarlo:

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.