19

I'm using Retrofit to make a POST Request in my web server.

However, I can't seem to get the response body when the response status is 422 (unprocessable entity). The response body is always null.

I want to know if I'm doing something wrong or if there's a workaround for this. Because I'm using the same json in the request with Postman, and it returns the body normally.

This is the method:

@Headers("Content-Type: application/vnd.api+json")
@POST("my_endpoint")
Call<JsonObject> postEntry(@Header("Authorization") String authorization, @Body JsonObject json);

The body is a JsonObject, I'm not serializing like the documentation say. But I don't think this is the problem.

Ali Khaki
  • 1,184
  • 1
  • 13
  • 24
Erick Filho
  • 1,962
  • 3
  • 18
  • 31
  • is your api returning some body in `422` errors?. Did you map in android? – Deividi Cavarzan Dec 07 '15 at 22:52
  • @DeividiCavarzan Yes, as I mentioned in the question, when I use Postman to make the request, it returns the body perfectly. I'm using the same json object in both places. – Erick Filho Dec 07 '15 at 23:06
  • @DeividiCavarzan And about mapping, yes I did. It actually executes the `onResponse(Response response, Retrofit retrofit)` in Callback with `response.body() == null`. – Erick Filho Dec 07 '15 at 23:07
  • @Erick your request is not completed so you face this issue .. – Amitsharma Dec 11 '15 at 12:01
  • @amitsharma Why it's not completed? – Erick Filho Dec 11 '15 at 12:03
  • Your Request Body is not completed there must be something missing or something wrong that's by you get status 422 , or can be bad server request.. – Amitsharma Dec 11 '15 at 12:46
  • 1
    I would recommend intercept your request while sending, with OkHttp interceptor and see if you are sending correct, compare it with Postman's – Jemshit Dec 12 '15 at 12:19

3 Answers3

16

By default, when your server is returning an error code response.body() is always null. What you are looking for is response.errorBody(). A common approach would be something like this:

    @Override
    public void onResponse(Response<JsonObject> response, Retrofit retrofit) {
        if (response.isSuccess()) {
            response.body(); // do something with this
        } else {
            response.errorBody(); // do something with that
        }
    }

If you need something advanced take a look at Interceptors and how to use them

tochkov
  • 2,917
  • 2
  • 15
  • 21
  • 2
    Thanks.. but my errorBody() is always null too.. I managed to make it work without retrofit. I'm awarding you the bounty because it's going to expire – Erick Filho Dec 16 '15 at 13:22
  • Ty. Via the interceptor you can examine what is going on under the hood and see the raw request and response. Anyway I'm glad you found a way :) – tochkov Dec 16 '15 at 14:08
1

I got the same error. My API was working using POSTMAN request but not working from Android retrofit call.

At first I was trying using @Field but it was getting error but later I've tried with @Body and it worked.

Sample Retrofit interface call

@POST("api/v1/app/instance")
Call<InstanceResponse> updateTokenValue(
        @HeaderMap Map<String, String> headers,
        @Body String body); 

and API calling code is:

 Map<String, String> headerMap=new HashMap<>();
        headerMap.put("Accept", "application/json");
        headerMap.put("Content-Type", "application/json");
        headerMap.put("X-Authorization","access_token");

        Map<String, String> fields = new HashMap<>();
        fields.put("app_name", "video");
        fields.put("app_version", "2.0.0");
        fields.put("firebase_token", "token");
        fields.put("primary", "1");

        ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
        Call<InstanceResponse> call = apiInterface.updateTokenValue(
                headerMap,new Gson().toJson(fields));
Swapon
  • 409
  • 6
  • 16
0

Well in this case you'll have to convert the response. Have a look at this link

All the steps are already provided in the link above.

For Kotlin users here is the code solution.

ErrorResponse.kt (This obviously depends on your error response)

import com.squareup.moshi.Json

data class ErrorResponse(

@Json(name="name")
val name: String? = null,

@Json(name="message")
val message: String? = null,

@Json(name="errors")
val errors: Errors? = null,

@Json(name="statusCode")
val statusCode: Int? = null
)

ApiFactory.kt (Let me know if you need the entire code)

fun parseError(response: Response<*>): ErrorResponse {
    val converter = ApiFactory.retrofit()
            .responseBodyConverter<ErrorResponse>(
                    ErrorResponse::class.java, arrayOfNulls<Annotation>(0)
            )

    val error: ErrorResponse

    try {
        error = converter.convert(response.errorBody()!!)!!
    } catch (e: IOException) {
        e.printStackTrace()
        return ErrorResponse()
    }

    return error
}

and in the Presenter (I use MVP)

GlobalScope.launch(Dispatchers.Main) {
        try {
            val response = ApiFactory.apiService.LOGIN(username, password)
                    .await()
            val body = response.body()
            body?.let {
            // Handle success or any other stuff
                if (it.statusCode == 200) {
                    mView.onSuccess(it.data!!)
                }
            } ?:
            // This is the else part where your body is null
            // Here is how you use it.
            // Pass the response for error handling
            mView.showMessage(ApiFactory.parseError(response).message!!)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

And thats how you roll it! That's All Folks!

Sagar Wankhede
  • 302
  • 3
  • 7