Como lidar com o corpo de resposta vazio com o Retrofit 2?

125

Recentemente, comecei a usar o Retrofit 2 e enfrentei um problema ao analisar o corpo de resposta vazio. Eu tenho um servidor que responde apenas com código http sem nenhum conteúdo dentro do corpo da resposta.

Como posso lidar apenas com meta informações sobre a resposta do servidor (cabeçalhos, código de status etc.)?

Yevgen Derkach
fonte

Respostas:

216

Editar:

Como Jake Wharton aponta,

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

é o melhor caminho a seguir em relação à minha resposta original -

Você pode simplesmente retornar a ResponseBody, que ignorará a análise da resposta.

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

Então, na sua ligação,

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
fonte
58
Ainda melhor: use Voidque não apenas tenha melhor semântica, mas seja (ligeiramente) mais eficiente no caso vazio e muito mais eficiente em um caso não vazio (quando você simplesmente não se importa com o corpo).
Jake Wharton
1
@JakeWharton Esse é um ótimo comportamento. Obrigado por apontar. Resposta atualizada.
iagreen
2
Ótima resposta. Um motivo para não usar o Void é se você tiver um recurso que retorne um corpo apenas quando a solicitação não tiver êxito e desejar converter o errorBody ResponseBody em algum tipo específico ou comum.
7
@JakeWharton Ótima sugestão de uso Void. Usar o Unitcódigo Kotlin ofereceria o mesmo benefício do VoidJava for Retrofit?
Akshay Chordiya
6
@ akshay-chordiya Acabei de verificar, Unitno Kotlin NÃO funciona, Voidno entanto. Presumo que exista um cheque codificado em algum lugar.
User3363866
40

Se você usa o RxJava, é melhor usar Completableneste caso

Representa um cálculo adiado sem nenhum valor, mas apenas indicação para conclusão ou exceção. A classe segue um padrão de evento semelhante ao Reactive-Streams: onSubscribe (onError | onComplete)?

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

na resposta aceita:

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

Se o nó de extremidade retornar o código de resposta de falha, ele ainda estará no onNexte você precisará verificar o código de resposta.

No entanto, se você usar Completable.

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

você terá apenas onCompletee onError. se o código de resposta for bem-sucedido, ele disparará do onCompletecontrário onError.

Ahmad El-Melegy
fonte
1
O que o onError Throwableargumento conterá, nesse caso? Acho isso mais limpo, mas muitas vezes ainda precisamos examinar o código de resposta e o corpo quanto a falhas.
big_m 17/06
24

Se você estiver usando o rxjava, use algo como:

@GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);
Geng Jiawen
fonte
Era isso que eu estava encontrando! Obrigado!
Sirelon 29/08/19
Eu usei ResposeBody com RxJava2 e Retrofit2 para solicitação PUT REST. Funcionou bem #
31317 Moti Bartov
1
Temos uma API de terminal que retorna o corpo vazio quando o sucesso e o corpo json quando o erro. Se estiver usando a resposta <Void>, como posso lidar com o caso de erro?
KeNVin Favo 22/02
Qual classe de resposta você usa aqui? Retrofit ou OKHttps?
Matthias
1
Não é uma boa opção se você lidar com as exceções de erro .. você recebe nenhuma exceção com esta abordagem, mas sim um JSON como resposta em caso de erro
Ovi Trif
0

Aqui está como eu o usei com Rx2 e Retrofit2, com solicitação PUT REST: Minha solicitação tinha um corpo json, mas apenas código de resposta http com corpo vazio.

O cliente 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;

}

A interface:

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

Então, para usá-lo:

    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
fonte