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.)?

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

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.


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 */);

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

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.