Quiero lanzar un error desde mi observable's map operador basado en una condición. Por ejemplo, si no se reciben los datos correctos de la API. Por favor, vea el siguiente código:
private userAuthenticate( email: string, password: string ) {
return this.httpPost(`${this.baseApiUrl}/auth?format=json&provider=login`, {userName: email, password: password})
.map( res => {
if ( res.bearerToken ) {
return this.saveJwt(res.bearerToken);
} else {
// THIS DOESN'T THROW ERROR --------------------
return Observable.throw('Valid token not returned');
}
})
.catch( err => Observable.throw(this.logError(err) )
.finally( () => console.log("Authentication done.") );
}
Básicamente como puede ver en el código, si la respuesta (objeto res) no tiene 'bearerToken' quiero lanzar un error. De manera que en mi subscripción va en el 2do parámetro (handleError) mencionado abajo.
.subscribe(success, handleError)
¿Alguna sugerencia?
Simplemente lanza el error dentro del operador map()
. Todas las devoluciones de llamada en RxJS se envuelven con bloques try-catch por lo que será capturado y luego enviado como una notificación de error.
Esto significa que no devuelve nada y sólo lanza el error:
map(res => {
if (res.bearerToken) {
return this.saveJwt(res.bearerToken);
} else {
throw new Error('Valid token not returned');
}
})
El throwError()
(antes Observable.throw()
en RxJS 5) es un Observable que sólo envía una notificación de error
pero map()
no le importa lo que devuelve. Incluso si devuelves un Observable de map()
será pasado como notificación next
.
Por último, probablemente no necesites utilizar .catchError()
(antes catch()
en RxJS 5). Si necesita realizar algún efecto secundario cuando se produce un error es mejor utilizar tap(null, err => console.log(err))
(antes do()
en RxJS 5) por ejemplo.
Ene 2019: Actualizado para RxJS 6
Si crees que throw new Error()
no es observable, puedes utilizar switchMap
:
// RxJS 6+ syntax
this.httpPost.pipe(switchMap(res => {
if (res.bearerToken) {
return of(this.saveJwt(res.bearerToken));
}
else {
return throwError('Valid token not returned');
}
});
o más concisamente:
this.httpPost.pipe(switchMap(res => (res.bearerToken) ?
of(this.saveJwt(res.bearerToken)) :
throwError('Valid token not returned')
));
El comportamiento será el mismo, sólo es diferente sintaxis.
Usted 're literalmente diciendo 'switch' desde el observable http en la tubería a un observable diferente, que es o bien sólo 'wrapping' el valor de salida, o un nuevo 'error' observable.
No olvide poner of
o recibirá mensajes de error confusos.
También la belleza de 'switchMap' es que puedes devolver toda una nueva 'cadena' de comandos si quisieras - para cualquier lógica que se necesite hacer con saveJwt
.
Aunque esta pregunta ya tiene respuesta, me gustaría compartir mi propio enfoque (aunque sólo difiera ligeramente del anterior).
Yo decidiría lo que se devuelve por separado de la asignación y viceversa. No estoy seguro de qué operador es el mejor para esto, así que usaré tap
.
this.httpPost.pipe(
tap(res => {
if (!res.bearerToken) {
throw new Error('Valid token not returned');
}
}),
map(res => this.saveJwt(res.bearerToken)),
);