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