with rxjava2 rxjava ejemplo android json retrofit rx-java

rxjava2 - retrofit rxjava android



Retrofit 2.0+RxJava+Error JSON body (2)

Soy bastante nuevo en RxJava y Retrofit y estoy tratando de escribir mis llamadas API con él. Todas las llamadas a la API devuelven un cuerpo JSON en caso de error que está en el formato general como,

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

Actualmente mi código para la llamada a la API de inicio de sesión (otros también son similares) es,

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

Cuando recibo un error en onError (), me gustaría descifrar automáticamente el JSON en el cuerpo del error a un POJO y usarlo. ¿Hay alguna manera de hacerlo preferiblemente en un solo lugar para todas las demás llamadas a la API? Cualquier ayuda es apreciada.


Deserializar puede ser un problema también. Puede usar el convertidor de actualización para deserializarlo (o hacerlo usted mismo).

Mi solución añade un poco a la de 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); } }); } }; }

y luego en 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 }


Sugeriría el uso de un Transformer reutilizable junto con el operador onErrorResumeNext para encapsular su lógica. Se vería algo como esto:

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

Preste atención a los comentarios en el código, ya que tiene que tomar la decisión si desea reportar la respuesta analizada en Error () o en Siguiente ().

Entonces puedes usar este transformador en cualquier parte de tus llamadas a la API de esta manera:

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