user2062024
user2062024

Reputation: 3661

Retrofit 2.0 - How to get response body for 400 Bad Request error?

So when I make a POST API call to my server, I get a 400 Bad Request error with JSON response.

{
    "userMessage": "Blah",
    "internalMessage": "Bad Request blah blah",
    "errorCode": 1
}

I call it by

Call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        //AA
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        //BB
    }
}

However the problem is that once I get the response, onFailure() is invoke so that //BB is called. Here, I have no way to access the JSON response. When I log the api request and response, it doesn't show JSON response at all. And Throwable t is IOException. However, strangely, when I make the same call on Postman, it does return the expected JSON response with 400 error code.

So my question is how can I get the json response when I get 400 Bad Request error? Should I add something to okhttpclient?

Thanks

Upvotes: 57

Views: 86559

Answers (11)

Rakesh Jha
Rakesh Jha

Reputation: 379

To handle 400 Bad Request error in response use following code.

         if (response.isSuccessful()) {
                if (response.body().getStatus().equals(1)) {
                    progressBar.setVisibility(View.GONE);
                    Toast.makeText(UploadPDFActivity.this, "Success", Toast.LENGTH_SHORT).show();
                }
            } else {
                int statusCode = response.code();
                Log.e("hello 4001", "else condition");

                if (statusCode == 400) {
                    progressBar.setVisibility(View.GONE);
                    if (!response.isSuccessful()) {
                        JSONObject jsonObject = null;
                        try {
                            jsonObject = new JSONObject(response.errorBody().string());
                            String entityId = jsonObject.getString("entity_id");
                            String errorMessage = jsonObject.getString("message");
                            String errorStatus = jsonObject.getString("status");
                            Log.e("hello entityId", entityId);
                            Log.e("hello errorMessage", errorMessage);
                            Log.e("hello errorStatus", errorStatus);
                            Toast.makeText(UploadPDFActivity.this, errorMessage, Toast.LENGTH_SHORT).show();

                        } catch (JSONException | IOException e) {
                            e.printStackTrace();
                        }
                    }

                     }
                      else {
                    // Handle other error cases
                }
            }
        }

Upvotes: 0

Rahul Kamble
Rahul Kamble

Reputation: 340

IF you are getting 400(Bad Request) by using retrofit first make sure are are setting input to API is Only Model class, If not then replace input request by Model class and then check you will get Success response.

@POST("api/users/CreateAccount")
Call<CreateAccount> createAccount(@Body CreateAccount model, @Header("Content-Type") String content_type);

Upvotes: 0

Sreejesh K Nair
Sreejesh K Nair

Reputation: 603

simply use

 if (throwable is HttpException && (throwable!!.code() == 400 || throwable!!.code()==404)){
                               var responseBody = throwable!!.response()?.errorBody()?.string()
                               val jsonObject = JSONObject(responseBody!!.trim())
                               var message = jsonObject.getString("message")
                               tvValMsg.set(message)
                            } 

Upvotes: 2

Dhaval Baldha
Dhaval Baldha

Reputation: 403

Handle ErrorResponse with your class object

Kotlin

val errorResponse = Gson().fromJson(response.errorBody()!!.charStream(), ErrorResponse::class.java)

Java

ErrorResponse errorResponse = new Gson().fromJson(response.errorBody.charStream(),ErrorResponse.class)

Upvotes: 14

Yasin Ka&#231;maz
Yasin Ka&#231;maz

Reputation: 6663

You can do it in your onResponse method, remember 400 is a response status not an error:

if (response.code() == 400) {              
    Log.v("Error code 400",response.errorBody().string());
}

And you can handle any response code except 200-300 with Gson like that:

if (response.code() == 400) {
   Gson gson = new GsonBuilder().create();
   ErrorPojoClass mError=new ErrorPojoClass();
   try {
       mError= gson.fromJson(response.errorBody().string(),ErrorPojoClass.class);
       Toast.makeText(context, mError.getDescription(), Toast.LENGTH_LONG).show();
   } catch (IOException e) {
       // handle failure to read error
   }        
}

Add this to your build.gradle : compile 'com.google.code.gson:gson:2.7'

If you want create Pojo class go to Json Schema 2 Pojo and paste your example Json response. Select source type Json and annotation Gson .

Upvotes: 67

Kuldeep Sakhiya
Kuldeep Sakhiya

Reputation: 3252

Here is the simplest solution,

If you want to handle the response from onFailure method:

@Override
public void onFailure(Call<T> call, Throwable t) {
    HttpException httpException = (HttpException) t;
    String errorBody = httpException.response().errorBody().string();
    // use Gson to parse json to your Error handling model class
    ErrorResponse errorResponse = Gson().fromJson(errorBody, ErrorResponse.class);
}

Or if you are using rxjava Observable with Kotlin, handle it from error body:

{ error ->
    val httpException :HttpException = error as HttpException
    val errorBody: String = httpException.response().errorBody()!!.string()
    // use Gson to parse json to your Error handling model class
    val errorResponse: ErrorResponse = 
       Gson().fromJson(errorBody, ErrorResponse::class.java)
}

Don't forget to properly handle json to class conversion (use try-catch if not sure).

Upvotes: 6

Sathish Kumar VG
Sathish Kumar VG

Reputation: 2172

You can try the below code to get 400 response. You can get error response from errorBody() method.

Call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        //get success and error response here
 if (response.code() == 400) {
                if(!response.isSuccessful()) {
                    JSONObject jsonObject = null;
                    try {
                        jsonObject = new JSONObject(response.errorBody().string());
                        String userMessage = jsonObject.getString("userMessage");
                        String internalMessage = jsonObject.getString("internalMessage");
                        String errorCode = jsonObject.getString("errorCode");
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        //get failure response here
    }
}
}

EDIT: Fixed method name from toString to string

Upvotes: 21

Keshav Gera
Keshav Gera

Reputation: 11244

public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
    DialogHelper.dismiss();

    if (response.isSuccessful()) {
        // Success
    } else {
        try {
            JSONObject jObjError = new JSONObject(response.errorBody().string());
            Toast.makeText(getContext(), jObjError.getString("message"), Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }
}

Upvotes: 9

Ekta Bhawsar
Ekta Bhawsar

Reputation: 746

First step:

Create your POJO class for error response. In my case, ApiError.java

public class ApiError {

    @SerializedName("errorMessage")
    @Expose
    private String errorMessage;

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage= errorMessage;
    }
}

Second Step:

Write below code in your api callback.

Call.enqueue(new Callback<RegistrationResponse>() {
     @Override
     public void onResponse(Call<RegistrationResponse> call, Response<RegistrationResponse> response) 
     {
         if (response.isSuccessful()) {
             // do your code here
         } else if (response.code() == 400) {
             Converter<ResponseBody, ApiError> converter =
                            ApiClient.retrofit.responseBodyConverter(ApiError.class, new Annotation[0]);

                    ApiError error;

                    try {
                        error = converter.convert(response.errorBody());
                        Log.e("error message", error.getErrorMessage());
                        Toast.makeText(context, error.getErrorMessage(), Toast.LENGTH_LONG).show();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
         }
     }

     @Override
     public void onFailure(Call<RegistrationResponse> call, Throwable t) {
         //do your failure handling code here
     }
}

Here ApiClient.retrofit is your retrofit instance which is static.

Upvotes: 5

Shivam Mathur
Shivam Mathur

Reputation: 111

This is how you can handle the response message I am handling for error 500 you can add as much you want

                switch (response.code()) {
                    case HttpURLConnection.HTTP_OK:
                        break;
                    case HttpURLConnection.HTTP_UNAUTHORIZED:
                        callback.onUnAuthentic();
                        break;
                    case HttpURLConnection.HTTP_INTERNAL_ERROR:
                        try {
                            String errorResponse = response.errorBody().string();
                            JSONObject object = new JSONObject(errorResponse);
                            String message = "Error";
                            if (object.has("Message"))
                                message = String.valueOf(object.get("Message"));
                            callback.onError(message);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        break;
                    case HttpURLConnection.HTTP_GATEWAY_TIMEOUT:
                    case HttpURLConnection.HTTP_CLIENT_TIMEOUT:
                    default:
                        callback.onNetworkError();
                        break;
                }

Upvotes: 1

Lukas
Lukas

Reputation: 1215

I got similar issue, but existing code was stick to RxJava 2 chain. Here's my solution:

   public static <T> Observable<T> rxified(final Call<T> request, final Class<T> klazz) {
    return Observable.create(new ObservableOnSubscribe<T>() {

        AtomicBoolean justDisposed = new AtomicBoolean(false);

        @Override
        public void subscribe(final ObservableEmitter<T> emitter) throws Exception {

            emitter.setDisposable(new Disposable() {
                @Override
                public void dispose() {
                    request.cancel();
                    justDisposed.set(true);
                }

                @Override
                public boolean isDisposed() {
                    return justDisposed.get();
                }
            });

            if (!emitter.isDisposed())
                request.enqueue(new Callback<T>() {
                    @Override
                    public void onResponse(Call<T> call, retrofit2.Response<T> response) {
                        if (!emitter.isDisposed()) {
                            if (response.isSuccessful()) {
                                emitter.onNext(response.body());
                                emitter.onComplete();

                            } else {
                                Gson gson = new Gson();
                                try {
                                    T errorResponse = gson.fromJson(response.errorBody().string(), klazz);
                                    emitter.onNext(errorResponse);
                                    emitter.onComplete();
                                } catch (IOException e) {
                                    emitter.onError(e);
                                }
                            }
                        }
                    }

                    @Override
                    public void onFailure(Call<T> call, Throwable t) {
                        if (!emitter.isDisposed()) emitter.onError(t);
                    }
                });
        }
    });
}

transforming 400-like responses into rx chain is pretty simple:

Call<Cat> request = catApi.getCat();
rxified(request, Cat.class).subscribe( (cat) -> println(cat) );

Upvotes: 4

Related Questions