Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom error handling with Retrofit

The REST Api I'm working with has custom codes and messages which are sent from server depending on the state, I would like to implement a custom Callback<T> that calls the success method only if the status code was 0.

Example SUCCESS Response received from server:

{
  "code":"0",
  "message":"success",
  "data": {
    "actual_data":"goes_here",
    "need_to_construct_objects","from_data"
  }
}

Example of FAILURE Response:

{
  "code":"301",
  "message":"wrong_password",
  "data": {
    "actual_data":"will_be_null",
    "no_need_to_construct_objects","from_data"
  }
}

code and message are returned by all requests, the data contains the actual response values, so I would like to do the following:

  1. Check the code and message and only call success() if code is 0.
  2. Call failure() if request failed or code != 0
  3. Construct custom objects based on the data response and pass them via success()

What is the best way to do this? I searched everywhere and could not find a good solution. The only one I got was to let all custom objects have the code and message fields too and check their values inside success(), but this could cause problems in future in case someone forgets to check the code before proceeding.

like image 850
Nima G Avatar asked Sep 25 '14 16:09

Nima G


People also ask

How do you handle errors in retrofit?

The solution is based on having our repository to return a sealed class of Result which can be Success, NetworkError or GenericError. When a generic error occurs (non-200), then it should try to convert the response body to an error response. This way we can show a specific error to the user.

What is custom error handling?

With a custom error handler, you can choose what to do with an error, whether it may be as simple as logging the error to console or sending the error to another error monitoring service. Below is an example of using a custom error handler to log errors: Java.

What is the purpose of a retrofit interface?

Retrofit is a type-safe REST client for Android, Java and Kotlin developed by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp.

What is retrofit and its benefits?

Retrofit is a type-safe HTTP networking library used for Android and Java. Retrofit was even better since it was super fast, offered better functionality, and even simpler syntax. Most developers since then have switched to using Retrofit to make API requests.


2 Answers

You can do that quickly by just making an abstract class that implements Callback, and declare your own abstract success and failure methods. The abstract class will handle Retrofit's standard callback methods, interpret the response and call the abstract methods accordingly.

I think another possible approach to this is to override Retrofit's Client interface to build your own Response object.

If you extend OkClient, it can go like this:

public class CustomClient extends OkClient {

    @Override public Response execute(Request request) throws IOException {
          Response originalRespone = super.execute(request);

          int statusCode = 0;
          //TODO: read JSON response here (using GSON or similar, and extract status code and message... etc.)

          //Convert the status code to HTTP standard status codes, according to the documentation you have.
          if(statusCode == 0) statusCode = 200;
          //Reconstruct a Response object
          return new Response(originalResponse.getUrl(), statusCode, originalResponse.getReason() /*should probably replace with parsed message*/, originalResponse.getHeaders(), originalResponse.getBody());
    }

This may be more work than handling your case in Callback, but I think it can help if at some point the API transitions to RESTful API conventions.

This solution comes with its own problem though, because that means the JSON conversion will run twice. One in your client, and another one by Retrofit. Not sure the correct way to do that at the moment. Probably something around TypedInput and a dummy Converter that passes already converted objects.

like image 144
Hassan Ibraheem Avatar answered Sep 20 '22 21:09

Hassan Ibraheem


Create a custom ResponseBodyConverter like this:

public class CustomResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final TypeAdapter<T> adapter;

CustomResponseBodyConverter(TypeAdapter<T> adapter) {
    this.adapter = adapter;
}

@Override
public T convert(ResponseBody value) throws IOException,CustomException {
    String json = "";
    try {
        String body = value.string();
        json = new JSONObject(body).getJSONObject("data").toString();
        int code = new JSONObject(body).getInt("code");
        String message = new JSONObject(body).getString("message");
        if(code != 0){
            throw new CustomException(message);
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    return adapter.fromJson(json);
   }
}
like image 36
Neo lee Avatar answered Sep 17 '22 21:09

Neo lee