Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rx java retrofit 2 error handling

I'm using retrofit 2 along with rx java

Situation: the app sends some request, then i get the response in json-format that is automatically converted to User dto, then in onNext method of rx java i receive the list of Users. What if i get some message from server like this: {"error":"can't get the list of users"}

how to handle this situation with retrofit 2 and rx?

      Subscription subscriptionBranches = model.getRepoBranches(owner, name)
                    .map(branchesMapper)
                    .subscribe(new Observer<List<Branch>>() {
                        @Override
                        public void onCompleted() {
                            ;
                        }

                        @Override
                        public void onError(Throwable e) {
                            if (e instanceof retrofit.HttpException) {
                                HttpException exception = (HttpException) e;
                            }
                            showError(e);
                        }

                        @Override
                        public void onNext(List<Branch> list) {
                            branchList = list;
                            view.showBranches(list);
                        }
                    });

            addSubscription(subscriptionBranches);
.....
    @Override
    public Observable<List<RepositoryDTO>> getRepoList(String name) {
        return apiInterface
                .getRepositories(name)
                .compose(applySchedulers());
    }
like image 484
Jenya Kyrmyza Avatar asked Mar 30 '16 12:03

Jenya Kyrmyza


2 Answers

Depending on the server response you might or might not get into your onError function. If the server returns a non-2XX http status code you'll get into the onError method. If on the other hand you get a 2XX http status code you'll enter onNext.

I'm assuming you can deal with the onNext bit and I'll explain how you can do it in the onError. It's important to realise that there are many ways of doing this and this is just an example that uses okhttp 3 and retrofit 2 beta4.

So retrofit2 says that every non-2XX http responses are HttpExceptions when using rxjava. This you already have it there in your code:

if (e instanceof retrofit.HttpException) {
   HttpException exception = (HttpException) e;
}

Now what you want to do is get the body of the response. This you can achieve by calling Response response = exception.response() in the HttpException you have there. With the response, getting the error body is quite straight forward. You just call response.errorBody(). You can then convert the body to a java object or just access it as a string.

Since you have a json error body as an example, here's how you can convert the response body to a java object:

new GsonConverterFactory().responseBodyConverter(type,
      new Annotation[0]).convert(response.errorBody());

where type is the class of the java object that represents the error.

So putting it all together, on your onError method you could write something like:

if (e instanceof retrofit.HttpException) {
   HttpException exception = (HttpException) e;
   Response response = exception.response();
   Converter<ResponseBody, MyError> converter = new GsonConverterFactory()
       .responseBodyConverter(MyError.class, Annotation[0]);
   MyError error = converter.convert(response.errorBody());
}

MyError is a model that represents the error json you have in your question.

like image 133
Fred Avatar answered Oct 19 '22 22:10

Fred


I believe in the case you mentioned you will just enter into your onError handling, because retrofit will fail to deserialize your response, as it's not formatted as a List. You could potentially handle your case through that based off of the exception type.

If you can't alter the api to return consistent response types, you will have to look into using TypedInput, and possibly a converter.

Additionally, while it may not be completely relevant/overkill to the situation at hand, TypeAdapters bear mentioning. They'll let you determine how retrofit deserializes gson on a per class basis.

Gson gson = new GsonBuilder()
    .registerTypeAdapter(MyClass.class, new MyAdapter())
    .create();

RestAdapter adapter = new RestAdapter.Builder()
    .setConverter(new GsonConverter(gson))
    .build();
like image 34
lase Avatar answered Oct 19 '22 22:10

lase