2015-11-28 7 views
16

Я довольно новичок в RxJava и Retrofit, и я пытаюсь написать с ним свои API-вызовы. Все API вызовы возвращают тело JSON на ошибку, которая в общем виде, как,Retrofit 2.0 + RxJava + Ошибка JSON body

{"errors":[{"code":100, "message":"Login/Password not valid", "arguments":null}]} 

В настоящее время мой код API вызова входа (другие тоже похожи) есть

mConnect.login(id, password) 
     .subscribe(new Subscriber<Token>() { 
      @Override 
      public void onCompleted() { 
       Log.d(TAG, "onCompleted()"); 
      } 

      @Override 
      public void onError(Throwable e) { 
       Log.e(TAG, "onError(): " + e); 
       if (e instanceof HttpException) { 
        // dump e.response().errorBody() 
       } 
      } 

      @Override 
      public void onNext(Token token) { 
       Log.d(TAG, "onNext(): " + token); 
      } 
     }); 

Когда я получить ошибку в onError(), я хотел бы автоматически декодировать JSON в корпусе ошибки POJO и использовать это. Есть ли способ сделать это предпочтительно в одном месте для всех других вызовов API. Любая помощь приветствуется.

ответ

5

Я предлагаю использовать многоразовый Transformer вместе с оператором onErrorResumeNext, чтобы инкапсулировать вашу логику. Было бы выглядеть примерно так:

<T> Observable.Transformer<T, T> parseHttpErrors() { 
    return new Observable.Transformer<T, T>() { 
     @Override 
     public Observable<T> call(Observable<T> observable) { 
      return observable.onErrorResumeNext(new Func1<Throwable, Observable<? extends T>>() { 
       @Override 
       public Observable<? extends T> call(Throwable throwable) { 
        if (throwable instanceof HttpException) { 
         HttpErrorPojo errorPojo = // deserialize throwable.response().errorBody(); 

         // Here you have two options, one is report this pojo back as error (onError() will be called), 
         return Observable.error(errorPojo); // in this case HttpErrorPojo would need to inherit from Throwable 

         // or report this pojo back as part of onNext() 
         return Observable.just(errorPojo); //in this case HttpErrorPojo would need to inherit from <T> 
        } 
        // if not the kind we're interested in, then just report the same error to onError() 
        return Observable.error(throwable); 
       } 
      }); 
     } 
    }; 
} 

Обратите внимание на комментарии в коде, так как вы должны принять решение, хотите ли вы сообщить разобранный ответ OnError() или onNext().

Затем вы можете использовать этот трансформатор в любом месте в вашем API вызовов, как это:

mConnect.login(id, password) 
     .compose(this.<Token>parseHttpErrors()) // <-- HERE 
     .subscribe(new Subscriber<Token>() { 
      @Override 
      public void onCompleted() { 
       Log.d(TAG, "onCompleted()"); 
      } 

      @Override 
      public void onError(Throwable e) { 
       Log.e(TAG, "onError(): " + e); 
       if (e instanceof HttpErrorPojo) { 
        // this will be called if errorPojo was reported via Observable.error() 
       } 
      } 

      @Override 
      public void onNext(Token token) { 
       Log.d(TAG, "onNext(): " + token); 
       if (token instanceof HttpErrorPojo) { 
        // this will be called if errorPojo was reported via Observable.just() 
       } 
      } 
     }); 
0

Deserialize может быть проблема тоже. Вы можете использовать retrofit converter to deserialize it (или сделать это самостоятельно).

Мое решение добавляет немного к одному из murki:

<T> Observable.Transformer<T, T> parseHttpErrors() { 
     return new Observable.Transformer<T, T>() { 
      @Override 
      public Observable<T> call(Observable<T> observable) { 
       return observable.onErrorResumeNext(new Func1<Throwable, Observable<? extends T>>() { 
        @Override 
        public Observable<? extends T> call(Throwable throwable) { 
         if (throwable instanceof HttpException) { 
          Retrofit retrofit = new Retrofit.Builder() 
            .baseUrl(SERVER_URL) // write your url here 
            .addConverterFactory(GsonConverterFactory.create()) 
            .build(); 
          Converter<ResponseBody, Error> errorConverter = 
            retrofit.responseBodyConverter(Error.class, new Annotation[0]); 
          // Convert the error body into our Error type. 
          try { 
           Error error = errorConverter.convert(((HttpException) throwable).response().errorBody()); 
           // Here you have two options, one is report this pojo back as error (onError() will be called), 
           return Observable.error(new Throwable(error.getMessage())); 
          } 
          catch (Exception e2) { 
           return Observable.error(new Throwable()); 
          } 

         } 
         // if not the kind we're interested in, then just report the same error to onError() 
         return Observable.error(throwable); 
        } 
       }); 
      } 
     }; 
    } 

, а затем на OnError(),

@Override 
public void onError(Throwable e) { 
    progressBar.setVisibility(View.GONE); // optional 
    if (!TextUtils.isEmpty(e.getMessage())) { 
      // show error as you like 
      return; 
    } 
    // show a default error if you wish 
}