2015-05-10 3 views
5

Я бы хотел, чтобы была модифицирована для повышения пользовательских исключений в зависимости от ответа сервера. Например, в следующем составе:RxJava and Retrofit - повышение пользовательских исключений в зависимости от ответа сервера

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

Я хотел бы поднять исключение для абонентов, если code ничего, кроме 0. Как можно с помощью Дооснащение и Rx? Я бы предпочел написать эту логику только один раз и применить ее ко всем наблюдаемым, возвращаемым путем модификации.

ответ

6

Я хотел бы сделать исключение для подписчиков, если код - это что-то иное, чем 0. Как это возможно с помощью Retrofit и Rx?

Вы можете использовать Observable.flatMap оператор:

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

    return Observable.just(response); 
}); 

Я очень предпочел бы писать эту логику только один раз и он применяется ко всем наблюдаемым возвращаемые дооснащения.

К сожалению, для этого нет возможности с помощью retrofit и rx-java. Вам есть, чтобы написать код для каждого звонка retrofit. Единственное, что вы можете сделать, это использовать метод Observable.compose и уменьшить количество шаблонов, которые вы на самом деле должны писать.

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

А вот ResponseTransformer класс:

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

Ну, как я уже говорил, нет никакого способа избежать шаблонный код, используя только retrofit и rxjava, но вы может обойти это с помощью dynamic proxies (обратите внимание, что вам больше не нужно звонить 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 класс:

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; 
    } 
} 
+0

Спасибо, очень полезно – Nima

+1

@Nima проверить мой обновленный ответ, он мог быть еще более полезным. –

1

Я хотел бы предложить другой подход.

Вам понадобится реализовать пользовательский клиент OkHttp с пользовательским 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(); 

В вашем перехватчике в зависимости от возвращаемого кода вы можете продолжить нормально или выбросить исключение.

Что-то вроде этого:

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; 
    } 
} 
+0

Это звучит как многообещающее решение, но есть одна большая проблема. Перехватчики 'okhttp' работают на уровне' http', а значение return 'response.code()' на самом деле является http-кодом, а не кодом из 'json'. Чтобы извлечь реальный код, вам нужно разобрать json (это плохая идея, потому что тогда он будет снова разобран конвертером «retrofit»). –

+0

@ Владымир Миронов да, это http-код. Если вы возвращаете простой json, всего 3 поля, как в вашем вопросе, разбор его дважды не должен быть проблемой. Если есть что-то более сложное, возможно, вам понадобится лучшее решение – LordRaydenMK

0

1 пользовательских 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 пользователя как:

api.youRequest() 
    .lift(new YourOperator()) 
    .subscribe(....); 
Смежные вопросы