2015-11-28 16 views
16

Jestem całkiem nowy dla RxJava i Retrofit i staram się pisać za pomocą wywołań API. Wszystkie wywołania API powrócić ciało JSON o błędzie, który jest w ogólnym formacie jak,Retrofit 2.0 + RxJava + Błąd JSON body

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

Obecnie mój kod do wywołania API logowania (inne są podobne) jest

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); 
      } 
     }); 

Kiedy dostać błąd w onError(), chciałbym automatycznie dekodować JSON w treści błędu do POJO zamiast tego i użyć tego. Czy istnieje sposób, aby to zrobić najlepiej w jednym miejscu dla wszystkich innych wywołań API. Każda pomoc jest doceniana.

Odpowiedz

5

Sugerowałbym użycie wielokrotnego użytku Transformer wraz z operatorem onErrorResumeNext do enkapsulacji logiki. Byłoby wyglądać następująco:

<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); 
       } 
      }); 
     } 
    }; 
} 

zwrócić uwagę na komentarze w kodzie, ponieważ trzeba podjąć decyzję, czy chcesz zgłosić przeanalizowany odpowiedź onError() lub onNext().

Następnie można użyć tego transformatora nigdzie w swoim API tak:

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

Cofnięcie może być problem zbyt. Możesz użyć retrofit converter to deserialize it (lub zrób to sam).

Proponowane rozwiązanie dodaje trochę do tej z 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); 
        } 
       }); 
      } 
     }; 
    } 

a następnie w 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 
} 
Powiązane problemy