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.
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;
}
}
I would suggest a different approach.
You will need to implement a custom OkHttp client with custom Interceptor.
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new MyInterceptor());
mAdapter = new RestAdapter.Builder().setEndpoint(Consts.ENDPOINT).setClient(new OkClient(client))
.setLogLevel(RestAdapter.LogLevel.BASIC).build();
In your interceptor depending on the code returned you can proceed normally or throw an exception.
Something like this:
public class MyInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if(response.code() == 0) {
throw new RuntimeException("Something went wrong!");
}
return response;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With