Nima Nima - 4 months ago 27
Android Question

RxJava and Retrofit - Raising custom exceptions depending on server response

I would like Retrofit to raise custom exceptions depending on the server response. For example in the following structure:

{
"code":0,
"message":"OK",
"data":{....}
}


I would like to raise an exception for subscribers if
code
is anything other than 0. How is it possible using Retrofit and Rx? I would much prefer to write this logic only once and have it applied to all observables returned by retrofit.

Answer

I would like to raise an exception for subscribers if code is anything other than 0. How is it possible using Retrofit and Rx?

You can use a Observable.flatMap operator:

api.request().flatMap(response -> {
    if (response.getCode() != 0) {
        return Observable.error(new Exception("Remote error occurred"));
    }

    return Observable.just(response);
});

I would much prefer to write this logic only once and have it applied to all observables returned by retrofit.

Unfortunately, there is not way to do it using retrofit and rx-java. You have to write the code above for every retrofit call. The only thing you can do is to use Observable.compose method and reduce the amount of boilerplate you actually have to write.

api.request().compose(new ResponseTransformer<Response>());

And here is the ResponseTransformer class:

public static class ResponseTransformer<T extends Response> implements Observable.Transformer<T, T> {
    @Override
    public Observable<T> call(final Observable<T> observable) {
        return observable.flatMap(response -> {
            if (response.getCode() != 0) {
                return Observable.error(new Exception("Remote error occurred"));
            }

            return Observable.just(response);
        });
    }
}

UPDATE

Well, as I said, there is no way to avoid boilerplate code using only retrofit and rxjava, but you can workaround it with dynamic proxies (note that you don't need to call compose anymore):

final Api api = restAdapter.create(Api.class);

final ClassLoader loader = api.getClass().getClassLoader();
final Class<?>[] interfaces = api.getClass().getInterfaces();

final Api proxy = (Api) Proxy.newProxyInstance(loader, interfaces, new ResponseInvocationHandler(api));

proxy.request().subscribe(response -> {
    System.out.println("Success!");
});

ResponseInvocationHandler class:

public static class ResponseInvocationHandler implements InvocationHandler {
    private final Object target;

    public ResponseInvocationHandler(final Object target) {
        this.target = target;
    }

    @Override
    @SuppressWarnings({"unchecked"})
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        final Object result = method.invoke(target, args);

        if (result instanceof Observable) {
            return Observable.class.cast(result).compose(new ResponseTransformer<>());
        }

        return result;
    }
}