Comment puis-je gérer un corps de réponse vide avec Retrofit 2?

125

Récemment, j'ai commencé à utiliser Retrofit 2 et j'ai rencontré un problème avec l'analyse du corps de réponse vide. J'ai un serveur qui ne répond qu'avec du code http sans aucun contenu dans le corps de la réponse.

Comment puis-je gérer uniquement les méta-informations sur la réponse du serveur (en-têtes, code d'état, etc.)?

Yevgen Derkach
la source

Réponses:

216

Éditer:

Comme le souligne Jake Wharton,

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

est la meilleure façon de procéder par rapport à ma réponse initiale -

Vous pouvez simplement renvoyer a ResponseBody, ce qui contournera l'analyse de la réponse.

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

Puis dans ton appel,

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
la source
58
Encore mieux: une utilisation Voidqui a non seulement une meilleure sémantique, mais qui est (légèrement) plus efficace dans le cas vide et beaucoup plus efficace dans un cas non vide (lorsque vous ne vous souciez pas du corps).
Jake Wharton
1
@JakeWharton C'est un excellent comportement. Merci de l'avoir signalé. Réponse mise à jour.
iagreen
2
Très bonne réponse. Une des raisons de ne pas utiliser Void est si vous avez une ressource qui ne renvoie un corps que lorsque la demande échoue et que vous souhaitez convertir le errorBody ResponseBody en un type spécifique ou commun.
7
@JakeWharton Excellente suggestion à utiliser Void. L'utilisation Unitdans le code Kotlin donnerait-elle le même avantage que Voiddans Java for Retrofit?
Akshay Chordiya
6
@ akshay-chordiya Je viens de vérifier, Unitdans Kotlin ne fonctionne PAS, Voidcependant. Je suppose qu'il y a un chèque codé en dur quelque part.
user3363866
41

Si vous utilisez RxJava, il est préférable d'utiliser Completabledans ce cas

Représente un calcul différé sans aucune valeur mais uniquement une indication de fin ou d'exception. La classe suit un modèle d'événement similaire à Reactive-Streams: onSubscribe (onError | onComplete)?

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

dans la réponse acceptée:

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

Si le point de terminaison renvoie le code de réponse d'échec, il sera toujours dans le onNextet vous devrez vérifier le code de réponse vous-même.

Cependant, si vous utilisez Completable.

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

vous n'aurez que onCompleteet onError. si le code de réponse réussit, il se déclenchera, onCompletesinon il se déclenchera onError.

Ahmad El-Melegy
la source
1
Que contiendra l' onError Throwableargument, dans ce cas? Je trouve cela plus propre, mais nous avons souvent encore besoin de regarder le code de réponse et le corps pour les échecs.
big_m
24

Si vous utilisez rxjava, utilisez quelque chose comme:

@GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);
Geng Jiawen
la source
C'est ce que je trouvais! Merci!
Sirelon
J'ai utilisé ResposeBody avec RxJava2 et Retrofit2 pour la demande PUT REST. Cela a bien fonctionné
Moti Bartov
1
Nous avons une API de point de terminaison qui renvoie un corps vide en cas de succès et un corps json en cas d'erreur. Si vous utilisez Response <Void>, comment puis-je gérer le cas d'erreur?
KeNVin Favo
Quelle classe de réponse utilisez-vous ici? Retrofit ou OKHttps?
Matthias
1
Ce n'est pas une bonne option si vous faites une gestion des erreurs sur les exceptions .. vous n'obtenez aucune exception avec cette approche, mais plutôt un JSON comme réponse en cas d'erreur
Ovi Trif
0

Voici comment je l'ai utilisé avec Rx2 et Retrofit2, avec la requête PUT REST: Ma requête avait un corps json mais juste un code de réponse http avec un corps vide.

Le 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'interface:

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

Alors pour l'utiliser:

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

}
Moti Bartov
la source