10

I'm trying to POST a JSONObject using the Retrofit library, but when I see the request at the receiving end, the content-length is 0.

In the RestService interface:

@Headers({
        "Content-type: application/json"
})
@POST("/api/v1/user/controller")
void registerController( 
     @Body JSONObject registrationBundle, 
     @Header("x-company-device-token") String companyDeviceToken, 
     @Header("x-company-device-guid") String companyDeviceGuid, 
     Callback<JSONObject> cb);

And it gets called with,

mRestService.registerController(
    registrationBundle, 
    mApplication.mSession.getCredentials().getDeviceToken(), 
    mApplication.mSession.getCredentials().getDeviceGuid(),
    new Callback<JSONObject>() {
        // ...
    }
)

And I'm certain that the registrationBundle, which is a JSONObject isn't null or empty (the other fields are certainly fine). At the moment the request is made, it logs out as: {"zip":19312,"useAccountZip":false,"controllerName":"mine","registrationCode":"GLD94Q"}.

On the receiving end of the request, I see that the request has Content-type: application/json but has Content-length: 0.

Is there any reason why sending JSON in the body like this isn't working? Am I missing something simple in using Retrofit?

JJD
  • 50,076
  • 60
  • 203
  • 339
adityajones
  • 601
  • 1
  • 4
  • 10
  • While this isn't an answer, I found that the best solution for me was to entirely forego the Retrofit library. I ended up using loopj's androidasync, that is very easy to use for all REST calls. You can post whatever, with whatever content-type and/or, and whatever type-specific callback you would need. Maybe this will help anybody looking at this. – adityajones Dec 27 '13 at 16:47

1 Answers1

31

By default, you don't need to set any headers if you want a JSON request body. Whenever you test Retrofit code, I recommend setting .setLogLevel(RestAdapter.LogLevel.FULL) on your instance of RestAdapter. This will show you the full request headers and body as well as the full response headers and body.

What's occurring is that you are setting the Content-type twice. Then you're passing a JSONObject, which is being passed through the GsonConverter and mangled to look like {"nameValuePairs":YOURJSONSTRING} where YOURJSONSTRING contains your complete, intended JSON output. For obvious reasons, this won't work well with most REST APIs.

You should skip messing with the Content-type header which is already being set to JSON with UTF-8 by default. Also, don't pass a JSONObject to GSON. Pass a Java object for GSON to convert.

Try this if you're using callbacks:

@POST("/api/v1/user/controller")
void registerController(
    @Body MyBundleObject registrationBundle,
    @Header("x-company-device-token") String companyDeviceToken,
    @Header("x-company-device-guid") String companyDeviceGuid,
    Callback<ResponseObject> cb);

I haven't tested this exact syntax.

Synchronous example:

@POST("/api/v1/user/controller")
ResponseObject registerController(
    @Body MyBundleObject registrationBundle,
    @Header("x-company-device-token") String companyDeviceToken,
    @Header("x-company-device-guid") String companyDeviceGuid);
JJD
  • 50,076
  • 60
  • 203
  • 339
colintheshots
  • 1,992
  • 19
  • 37
  • I think this only applies to Retrofit < v2 – Julian A. Mar 10 '16 at 21:08
  • 1
    Yes. Look at the date. 12/31/2013. – colintheshots Mar 12 '16 at 21:02
  • Is there some way to not have to send a body? Something like FormUrlEncoded, but the parameters should be sent in the body. – IcyFlame Aug 10 '16 at 13:14
  • The original question was asking about passing JSON as the entire body of the POST. This is a common need for APIs. If you only want to include JSON for setting a parameter of a FormUrlEncoded POST body, you could use Field or FieldMap annotations instead. – colintheshots Aug 11 '16 at 15:22