Getting JSON from a RetrofitError Object Using Retrofit

I am using the Retrofit library to make REST calls for the service I am using.

If I make an API call to my service and have a failure, the service returns a JSON bit along with a standard HTTP error message. Using the RetrofitError object included in the failure callback, I can find the HTTP status code and a few other things, however I cannot get the JSON that the service sends back.

For example, let's say I call the API where I am trying to create a user. If the username already exists, the service will return an 400 error code along with some JSON as follows:

 {"error":"Username already in use"} 

Since the 400 simple error code is not specific enough, I really need access to the returned JSON.

Does anyone know how I can get JSON data? I tried looking at each field in a RetrofitError object and cannot find it anywhere. Is there anything extra I need to do?

+33
android retrofit
Dec 16 '13 at 23:00
source share
9 answers

You can use the getBodyAs method of the RetrofitError object. It converts the response to a Java object in a manner similar to other advanced transformations. First, define a class that describes your JSON error response:

 class RestError { @SerializedName("code") public int code; @SerializedName("error") public String errorDetails; } 

Then use the previously mentioned method to get an object that describes the error in more detail.

 catch(RetrofitError error) { if (error.getResponse() != null) { RestError body = (RestError) error.getBodyAs(RestError.class); log(body.errorDetails); switch (body.code) { case 101: ... case 102: ... } } } 



Retrofit 2.0 has changed the way you get error responses. You will need to get the desired converter using the responseBodyConverter method and use it to convert the body of the response error. To exclude exception handling, this would be:

 Converter<ResponseBody, RestError> converter = retrofit.responseBodyConverter(RestError.class, new Annotation[0]); RestError errorResponse = converter.convert(response.errorBody()); 
+100
Jan 13 '14 at 23:28
source share

Try this code

 @Override public void failure(RetrofitError error) { String json = new String(((TypedByteArray)error.getResponse().getBody()).getBytes()); Log.v("failure", json.toString()); } 

with Retrofit 2.0

 @Override public void onFailure(Call<Example> call, Throwable t) { String message = t.getMessage(); Log.d("failure", message); } 
+19
Sep 20 '14 at 14:11
source share

@LukaCiko the answer does not work now for me in version 1.6.1. Here's how I do it now:

  @Override public void failure(RetrofitError retrofitError) { String json = new String(((TypedByteArray)retrofitError.getResponse().getBody()).getBytes()); //own logic for example ExampleHandlerError error = new Gson().fromJson(json, ExampleHandlerError.class); } 
+5
03 Sep '14 at 9:43
source share
 JsonElement jsonElement = (JsonElement) retrofitError.getBodyAs(JsonElement.class); 
+1
Oct 21 '15 at 21:08
source share

Using this, you can get the error body

  if (response != null && response.errorBody() != null) { JSONObject jsonObject = new JSONObject(response.errorBody().string()); String error = jsonObject.getString("error"); } 
+1
Jul 13. '16 at 17:05
source share

An easier way to do this is to create a class that can parse / transform for you, one that you can call anytime, anywhere.

 public class ApiError { public String error = "An error occurred"; public ApiError(Throwable error) { if (error instanceof HttpException) { String errorJsonString = null; try { errorJsonString = ((HttpException) error).response().errorBody().string(); } catch (IOException e) { e.printStackTrace(); } JsonElement parsedString = new JsonParser().parse(errorJsonString); this.error = parsedString.getAsJsonObject() .get("error") .getAsString(); } else { this.error = error.getMessage() != null ? error.getMessage() : this.error; } } } 

Now you can use it anywhere, for example

 ApiError error = new ApiError(error) error.error 

This is all of this blog post (which I wrote).

+1
Aug 13 '17 at 2:36 on
source share

So, after a little hunting, I found the answer.

Here is my failure:

 @Override public void failure(RetrofitError retrofitError) { String serverError = null; try { serverError = extractServerError(retrofitError.getResponse().getBody().in()); } catch (Exception e) { Log.e("LOG_TAG", "Error converting RetrofitError server JSON", e); } if (serverError!=null) { Log.i(LOG_TAG, serverError); } Intent intent = new Intent(ACTION_REGISTRATION_ERROR); intent.putExtra("ServerError", serverError); LocalBroadcastManager.getInstance(FamDooApplication.CONTEXT).sendBroadcastSync(intent); } 

And here is the method that is called to retrieve the server error:

 public static String extractServerError(java.io.InputStream is) { String serverError = null; String serverErrorDescription = null; try { String s = convertStreamToString(is); JSONObject messageObject = new JSONObject(s); serverError = messageObject.optString("error"); serverErrorDescription = messageObject.optString("error_description"); if (serverErrorDescription!=null && !serverErrorDescription.equals("")) { return serverErrorDescription; } else { return serverError; } //String serverStack = messageObject.getString("stack"); } catch (Exception e) { Log.e("Basemodel", "Error converting RetrofitError server JSON", e); } return ""; } 

This will retrieve the error information sent by the service, which is encoded in JSON.

0
Dec 16 '13 at 23:20
source share

I compile a lot of answers and wrote code to achieve something more enjoyable:

 { "errors": { "email": [ "Email not valid.", "Unable to find the user toto@toto.fr." ] } } 

I take all the elements in the "email" and display them in concatenation with the Guava Joiner:

 String json = new String(((TypedByteArray)error.getResponse() .getBody()).getBytes()); Map<String, Object> map = new Gson().fromJson( json, new TypeToken<Map<String, Map<String, List<String>>>>() {}.getType()); try { List<String> errorsEmail = (List<String>) ((Map)map.get("errors")).get("email"); Toast.makeText(getApplicationContext(), Joiner.on("\n") .join(errorsEmail), Toast.LENGTH_SHORT).show(); } catch(Exception e){ Log.e(Constants.TAG, e.getMessage()); } 
0
Sep 12 '14 at 17:09
source share

I had the same thing. My problem was in the long field, which came from the server as an empty String "" . "Retrofitting" NumberFormatException because it looks like Gson is not converting an empty string to long (I would suggest doing it 0L or something else). So I had to change:

 long errorCode; 

to

 String errorCode; 

As already mentioned, I did not have access to the JSON message when debugging. I finally found an error using the RequestMaker page, maybe it helps someone else

http://requestmaker.com/

0
Jul 15 '15 at 10:25
source share



All Articles