Since I started with angular2, I had the settings of my services to return Observable of T. In the service, I will have a map () call, and the components using these services will just use subscribe () to wait for an answer. For these simple scenarios, I really don't need to delve into rxjs, so everything was fine.
Now I want to achieve the following: I use Oauth2 authentication with update tokens. I want to create an api service that all other services will use and which will transparently handle the update token when error 401 is returned. So, in the case of 401, I first retrieve the new token from the OAuth2 endpoint and then repeat my request with the new token . Below is the code that works great with promises:
request(url: string, request: RequestOptionsArgs): Promise<Response> { var me = this; request.headers = request.headers || new Headers(); var isSecureCall: boolean = true; //url.toLowerCase().startsWith('https://'); if (isSecureCall === true) { me.authService.setAuthorizationHeader(request.headers); } request.headers.append('Content-Type', 'application/json'); request.headers.append('Accept', 'application/json'); return this.http.request(url, request).toPromise() .catch(initialError => { if (initialError && initialError.status === 401 && isSecureCall === true) { // token might be expired, try to refresh token. return me.authService.refreshAuthentication().then((authenticationResult:AuthenticationResult) => { if (authenticationResult.IsAuthenticated == true) { // retry with new token me.authService.setAuthorizationHeader(request.headers); return this.http.request(url, request).toPromise(); } return <any>Promise.reject(initialError); }); } else { return <any>Promise.reject(initialError); } }); }
In the above code, authService.refreshAuthentication () will select a new token and store it in localStorage. authService.setAuthorizationHeader will set the Authorization header to a previously updated token. If you look at the catch method, you will see that it returns a promise (for the update token), which in turn will return another promise (for the actual 2nd request attempt).
I tried to do this without resorting to promises:
request(url: string, request: RequestOptionsArgs): Observable<Response> { var me = this; request.headers = request.headers || new Headers(); var isSecureCall: boolean = true; //url.toLowerCase().startsWith('https://'); if (isSecureCall === true) { me.authService.setAuthorizationHeader(request.headers); } request.headers.append('Content-Type', 'application/json'); request.headers.append('Accept', 'application/json'); return this.http.request(url, request) .catch(initialError => { if (initialError && initialError.status === 401 && isSecureCall === true) { // token might be expired, try to refresh token return me.authService.refreshAuthenticationObservable().map((authenticationResult:AuthenticationResult) => { if (authenticationResult.IsAuthenticated == true) { // retry with new token me.authService.setAuthorizationHeader(request.headers); return this.http.request(url, request); } return Observable.throw(initialError); }); } else { return Observable.throw(initialError); } }); }
The code above does not fulfill what I expect: in the case of a 200 response, it correctly returns the answer. However, if he catches 401, he will successfully extract the new token, but the wil subscription will eventually receive the observable instead of the answer. I guess this is an unexplored Observable that should try again.
I understand that translating the promised way to work with the rxjs library is probably not the best way, but I could not understand that "this is all a stream." I tried several other solutions, including flatmap, retryWhen, etc., but didn’t get far, so some help to evaluate.