RxJava and Retrofit - raising user exceptions depending on server response

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

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

I would like to make an exception for subscribers if code is something other than 0. How is this possible with Retrofit and Rx? I would prefer to write this logic only once and apply it to all observables returned by modification.

+5
source share
3 answers

I would like to create an exception for subscribers if the code is something other than 0. How is this possible using Retrofit and Rx?

You can use the 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 prefer to write this logic only once and apply it to all observables returned by modification.

Unfortunately, there is no way to do this using retrofit and rx-java . You have to write the code above for each retrofit call. The only thing you can do is use the Observable.compose method and reduce the number of templates you really need 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 the template code using only retrofit and rxjava , but you can bypass it using dynamic proxies (note that you no longer need to call compose ):

 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; } } 
+6
source

I suggest a different approach.

You will need to implement the OkHttp user client with a 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 return code, you can continue normally or throw an exception.

Something like that:

 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; } } 
+1
source

1 user Observable.Operator :

 public class YourOperator implements Observable.Operator{ public void onNext(Data data){ if (data.code != 0 ){ //raise your custom Exception } } public void onError(Throwable e){ //handler Exception } } 

2 users:

 api.youRequest() .lift(new YourOperator()) .subscribe(....); 
0
source

All Articles