2015-11-28 16 views
16

Sono abbastanza nuovo per RxJava e Retrofit e sto provando a scrivere le mie chiamate API con esso. Tutte le chiamate API restituiscono un corpo JSON in caso di errore, che è nel formato generale,Retrofit 2.0 + RxJava + Errore corpo JSON

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

Attualmente il mio codice per la chiamata API di accesso (gli altri sono simili) è,

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

Quando ho ottenere un errore su onError(), mi piacerebbe decodificare automaticamente il JSON nel corpo dell'errore in un POJO e usarlo. C'è un modo per farlo preferibilmente in un posto per tutte le altre chiamate API. Qualsiasi aiuto è apprezzato.

risposta

5

Vorrei suggerire l'uso di un Transformer riutilizzabile insieme all'operatore onErrorResumeNext per incapsulare la logica. Sarebbe simile a questa:

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

Prestare attenzione ai commenti nel codice, dal momento che si deve prendere la decisione se si desidera segnalare la risposta analizzato onError() o onNext().

quindi è possibile utilizzare questo trasformatore in qualsiasi parte le chiamate API in questo modo:

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 può essere un problema troppo. Puoi usare lo retrofit converter to deserialize it (o farlo tu stesso).

La mia soluzione aggiunge un po 'a quello da 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); 
        } 
       }); 
      } 
     }; 
    } 

e poi alla 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 
}