82

I called PATCH web service using Retrofit2 but onResponse is not called and the onFailure is called Despite of the operation of the service is succeeded on the server side perfectly

And whenever,I tried to use fiddler to check the service its working , i found the problem is that serializing the coming response of the service and when using fiddler i found that no content of the JSON response so the Retrofit service assumed that its failed because there is no content and it cannot serialize the EMPTY content and give me this error

java.io.EOFException: End of input at line 1 column 1

Fiddler raw Response

HTTP/1.1 200 OK
Server: nginx/1.9.4
Date: Wed, 02 Mar 2016 09:55:55 GMT
Content-Type: application/json
Content-Length: 0
Connection: close
Status: 200 OK
X-Content-Type-Options: nosniff

Fiddler Json Response is empty

webservice in java

Call<Object> call = TimeCapp.services.accept_invited_alerts(HomeActivity.api_token, alert_id);

call.enqueue(new Callback<Object>()
{
    @Override
    public void onResponse (Call<Object> call, Response<Object> response)
    {
        if (response.isSuccess()) {
            String x = response.body();
        }
    }
    @Override
    public void onFailure (Call<Object>call, Throwable t)
    {
        String x = t.getMessage();//java.io.EOFException: End of input at line 1 column 1
    }
}

I tried to replace object with String,JsonObject,emptyCalssBody .... but its failed

the interface of the webservice

@PATCH("alerts/{alert_id}/accept")
Call<Object> accept_invited_alerts(@Header("X-Api-Token") String  
api_token, @Path("alert_id") int alert_id);
Mina Farid
  • 5,041
  • 4
  • 39
  • 46
  • 1
    just return void instead, if the body is empty – Bhargav Mar 02 '16 at 10:48
  • i replaced the object type with Void type in java code and not changed in webservice return ,thanks for your hint so you can post the answer to accept and mark it :) @Bhargav – Mina Farid Mar 02 '16 at 11:00

7 Answers7

127

just return void instead, if the body is empty

@PATCH("alerts/{alert_id}/accept") Call<Void> accept_invited_alerts(@Header("X-Api-Token") String api_token, @Path("alert_id") int alert_id);

for retrofit with Rx java you can use something like this

@PATCH("alerts/{alert_id}/accept") Observable<Response<Void>> accept_invited_alerts(@Header("X-Api-Token") String api_token, @Path("alert_id") int alert_id);

EDIT: For kotlin

@PATCH("alerts/{alert_id}/accept")
fun accept_invited_alerts(@Header("X-Api-Token")  api_token: String, @Path("alert_id") alert_id: Int): Call<Unit>

and

@PATCH("alerts/{alert_id}/accept")
fun accept_invited_alerts(@Header("X-Api-Token") api_token: String, @Path("alert_id") alert_id: Int): Observable<Response<Unit>>
Bhargav
  • 8,118
  • 6
  • 40
  • 63
29

You can create NullOnEmptyConverterFactory.class :

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;


public class NullOnEmptyConverterFactory extends Converter.Factory {

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
        return new Converter<ResponseBody, Object>() {
            @Override
            public Object convert(ResponseBody body) throws IOException {
                if (body.contentLength() == 0) return null;
                return delegate.convert(body);               
            }
        };
    }
}

and add to code create. For ex:

 UploadImageNghiemThuApi uploadService = new Retrofit.Builder()
            .baseUrl(Config.URL+"/")
            .client(okHttpClient)
            // -----add here-------
            .addConverterFactory(new NullOnEmptyConverterFactory())
            //---------------------
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(UploadImageNghiemThuApi.class);

I hope it can help your problem. Thanks!

EDIT !!!

For a kotlin usecase you can check this

class NullOnEmptyConverterFactory : Converter.Factory() {
override fun responseBodyConverter(
    type: Type,
    annotations: Array<Annotation>,
    retrofit: Retrofit
): Converter<ResponseBody, *> {
    val delegate: Converter<ResponseBody, *> =
        retrofit.nextResponseBodyConverter<Any>(this, type, annotations)
    return Converter { body -> if (body.contentLength() == 0L) null else delegate.convert(body) }
}

}

Austine Gwa
  • 804
  • 1
  • 9
  • 18
Thientvse
  • 1,753
  • 1
  • 14
  • 23
  • This did the job for me as I still want to read a potential error response body, but in success case there's no content, just a 200, cheers – darnmason Jun 14 '19 at 07:04
  • This is the real way how it should be handled. Kudos to the author! :) – rzaaeeff Jul 19 '19 at 13:52
  • 5
    Thank you! This was really helpful. In case anyone's looking for the kotlin equivalent https://gist.github.com/rajivrnair/ddaef641d85b1dc402c72ac7e1cac0b6 – Rajiv Sep 09 '20 at 12:25
  • Will it help for end of stream error by any chance? – kvaruna Feb 17 '21 at 18:46
6

Thank you very much.

Api

@FormUrlEncoded
@POST("/rebu/insertusuario.php")
Call<Void> insertLogin(
        @Field("email") String email,
        @Field("senha") String senha,
        @Field("codCondutor") Long codCondutor
);

Class:

Call call = service.insertLogin(login.getEmail(), login.getSenha(), login.getCodCondutor());
Morteza Jalambadani
  • 2,190
  • 6
  • 21
  • 35
0

For me in laravel I was not sending any response after function completed just adding the following lines on server side helped. This can be adapted to the language you are using. Just send some response from server side.

use Response;

return Response::json(array(
            'result' => 'success',

        ));
DragonFire
  • 3,722
  • 2
  • 38
  • 51
0

I was facing a similar issue in my Spring Boot service. The issue was with the Content-Length header value. Anything more than the specified no of bytes was causing the problem.

After removing the header, the issue got resolved.

You can either remove the header or try having a significant large value.

Biswajit
  • 1
  • 2
0

To solve this kind of(Retrofit2 error java.io.EOFException: End of input at line 1 column 1) exception which is an empty body exception. Yo need to pass the void.

  private void callCheck(){

    Api api = service.getTestClient().create(Api.class);

    Call<Void> call = api.SendRequest(Constant.client_id,Constant.client_secret,Constant.apiUserName);;

    call.enqueue(new Callback<Void>() {
        @Override
        public void onResponse(Call<Void> call, retrofit2.Response<Void> response) {

            if (response.isSuccessful()) {
               //Perform according to it...
               // Here I am getting the response from the header...
            } else {
            // Is to handle the error body if an user provided the wrong values in the header...
                try {
           //these are for the error body
                    JSONObject jsonObject =new JSONObject(response.errorBody().string());
                    Log.d("ErrorBody:", jsonObject.toString());


                   JSONObject responseObj = jsonObject.getJSONObject("fault");
                    String fault = responseObj.getString("faultstring");
                    Log.d(TAG, "Fault: " + fault);

                    JSONObject responseObjDetail = responseObj.getJSONObject("detail");
                    String detail = responseObjDetail.getString("errorcode");
                    Log.d(TAG, "Detail: " + detail);
                }catch (JSONException | IOException e) {e.printStackTrace();}

            }
        }
0

This error can happen in Kotlin if you're trying to use the Retrofit Result class such as in Single<Result<Void>> but instead of using the retrofit2.adapter.rxjava2.Result class your code may be incorrectly using the kotlin.Result class.

If this is the problem, add the import line import retrofit2.adapter.rxjava2.Result so that your code will use the right class for Result.

Trevor
  • 1,349
  • 10
  • 16