Так как я начал с angular2, у меня были настройки моих сервисов, чтобы вернуть Observable of T. В службе у меня будет вызов map(), а компоненты, использующие эти службы, будут просто использовать subscribe(), чтобы дождаться ответ. Для этих простых сценариев мне действительно не нужно копаться в rxjs, так что все было в порядке.
Теперь я хочу добиться следующего: я использую аутентификацию Oauth2 с токенами обновления. Я хочу создать службу api, которую будут использовать все другие службы, и которая будет прозрачно обрабатывать токен обновления, когда возвращается ошибка 401. Итак, в случае с 401 я сначала извлекаю новый токен из конечной точки OAuth2, а затем повторю свой запрос с новым токеном. Ниже приведен код, который отлично работает, с 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);
}
});
}
В приведенном выше коде authService.refreshAuthentication() выберет новый токен и сохранит его в localStorage. authService.setAuthorizationHeader установит заголовок "Авторизация" на ранее обновленный токен. Если вы посмотрите на метод catch, вы увидите, что он возвращает обещание (для токена обновления), которое в свою очередь вернет другое обещание (для фактической 2-й попытки запроса).
Я попытался сделать это, не прибегая к 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);
}
});
}
Приведенный выше код не выполняет то, что я ожидаю: в случае ответа 200 он правильно возвращает ответ. Однако, если он поймает 401, он успешно извлечет новый токен, но подписка wil в конечном итоге получит наблюдаемое вместо ответа. Я предполагаю, что это неисследованный Observable, который должен повторить попытку.
Я понимаю, что перевод обетованного способа работы с библиотекой rxjs, вероятно, не самый лучший способ, но я не мог понять, что "все это поток". Я попробовал несколько других решений, в том числе flatmap, retryWhen и т.д., Но не получил далеко, поэтому некоторые помогают оценить.