Wie kann ich mit Retrofit 2 mit leeren Antwortkörpern umgehen?


125

Vor kurzem habe ich angefangen, Retrofit 2 zu verwenden, und ich hatte ein Problem mit dem Parsen des leeren Antwortkörpers. Ich habe einen Server, der nur mit http-Code ohne Inhalt im Antworttext antwortet.

Wie kann ich nur Metainformationen über die Serverantwort (Header, Statuscode usw.) verarbeiten?

Antworten:


216

Bearbeiten:

Wie Jake Wharton betont,

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

ist der beste Weg gegen meine ursprüngliche Antwort -

Sie können einfach a zurückgeben ResponseBody, wodurch das Parsen der Antwort umgangen wird.

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

Dann in Ihrem Anruf,

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
Noch besser: Verwendung, Voiddie nicht nur eine bessere Semantik aufweist, sondern im leeren Fall (geringfügig) effizienter und im nicht leeren Fall weitaus effizienter ist (wenn Sie sich nur nicht um den Körper kümmern).
Jake Wharton

1
@ JakeWharton Das ist tolles Verhalten. Vielen Dank für den Hinweis. Antwort aktualisiert.
iagreen

2
Gute Antwort. Ein Grund, Void nicht zu verwenden, besteht darin, dass Sie über eine Ressource verfügen, die nur dann einen Text zurückgibt, wenn die Anforderung nicht erfolgreich ist, und den errorBody ResponseBody in einen bestimmten oder allgemeinen Typ konvertieren möchten.

7
@ JakeWharton Toller Vorschlag zu verwenden Void. Würde die Verwendung Unitin Kotlin-Code den gleichen Vorteil wie Voidin Java für Retrofit bieten?
Akshay Chordiya

6
@ akshay-chordiya Ich habe gerade nachgesehen, Unitin Kotlin funktioniert das NICHT, Voidfunktioniert aber. Ich gehe davon aus, dass es irgendwo einen fest codierten Scheck gibt.
user3363866

40

Wenn Sie RxJava verwenden, ist es Completablein diesem Fall besser, es zu verwenden

Stellt eine verzögerte Berechnung ohne Wert dar, aber nur als Hinweis auf Abschluss oder Ausnahme. Die Klasse folgt einem ähnlichen Ereignismuster wie Reactive-Streams: onSubscribe (onError | onComplete)?

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

in der akzeptierten Antwort:

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

Wenn der Endpunkt einen Fehlerantwortcode zurückgibt, befindet er sich weiterhin im onNextund Sie müssen den Antwortcode selbst überprüfen.

Wenn Sie jedoch verwenden Completable.

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

Sie werden nur onCompleteund haben onError. Wenn der Antwortcode erfolgreich ist, wird er ausgelöst, onCompleteandernfalls wird er ausgelöst onError.


1
Was wird das onError ThrowableArgument in diesem Fall enthalten? Ich finde das sauberer, aber wir müssen oft noch den Antwortcode und den Text auf Fehler überprüfen.
big_m

24

Wenn Sie rxjava verwenden, verwenden Sie Folgendes:

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

Das habe ich gefunden! Vielen Dank!
Sirelon

Ich habe ResposeBody mit RxJava2 und Retrofit2 für PUT REST-Anforderungen verwendet. Es hat gut funktioniert
Moti Bartov

1
Wir haben eine Endpunkt-API, die bei Erfolg einen leeren Körper und bei einem Fehler einen json-Körper zurückgibt. Wie kann ich mit dem Fehlerfall umgehen, wenn ich Response <Void> verwende?
KeNVin Favo

Welche Antwortklasse verwenden Sie hier? Nachrüstung oder OKHttps?
Matthias

1
Es ist keine gute Option, wenn Sie Fehlerbehandlung für Ausnahmen durchführen. Sie erhalten keine Ausnahme mit diesem Ansatz, sondern einen JSON als Antwort auf Fehler
Ovi Trif

0

So habe ich es mit Rx2 und Retrofit2 mit PUT REST-Anfrage verwendet: Meine Anfrage hatte einen JSON-Body, aber nur einen http-Antwortcode mit einem leeren Body.

Der Api-Client:

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;

}

Die Schnittstelle:

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

Dann, um es zu benutzen:

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

}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.