Обработка токенов обновления с использованием rxjs

Поскольку я начал с angular2, у меня были настройки моих сервисов, чтобы вернуть Observable of T. В службе у меня будет вызов map (), а компоненты, использующие эти службы, просто будут использовать subscribe () для ожидания ответа. Для этих простых сценариев мне действительно не нужно копаться в rxjs, так что все было в порядке.

Теперь я хочу добиться следующего: я использую аутентификацию Oauth2 с токенами обновления. Я хочу создать службу api, которую будут использовать все другие службы, и которая будет прозрачно обрабатывать токен обновления, когда возвращается ошибка 401. Итак, в случае с 401 я сначала извлекаю новый токен из конечной точки OAuth2, а затем повторю свой запрос с новым токеном. Ниже приведен код, который отлично работает, с обещаниями:

request(url: string, request: RequestOptionsArgs): Promise { 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 Promise.reject(initialError); }); } else { return Promise.reject(initialError); } }); } 

В приведенном выше коде authService.refreshAuthentication () будет извлекать новый токен и хранить его в localStorage. authService.setAuthorizationHeader установит заголовок «Авторизация» на ранее обновленный токен. Если вы посмотрите на метод catch, вы увидите, что он возвращает promise (для токена обновления), которое в свою очередь вернет другое promise (для фактической 2-й попытки запроса).

Я попытался сделать это, не прибегая к обещаниям:

 request(url: string, request: RequestOptionsArgs): Observable { 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, вероятно, не самый лучший способ, но я не мог понять, что «все это stream». Я пробовал несколько других решений, включая flatmap, retryWhen и т. Д., Но не получил далеко, так что некоторая помощь приветствуется.

Из быстрого взгляда на ваш код я бы сказал, что ваша проблема заключается в том, что вы не сглаживаете Observable которая возвращается из службы refresh .

Оператор catch ожидает, что вы Observable что он будет конкатенации в конце неудачного Observable, чтобы Observer нисходящего streamа не знал разницы.

В случае, отличном от 401, вы делаете это правильно, возвращая Observable, который возвращает исходную ошибку. Однако в обновленном случае вы возвращаете Observables а вместо одиночных значений генерирует больше Observables .

Я бы предложил вам изменить логику обновления:

  return me.authService .refreshAuthenticationObservable() //Use flatMap instead of map .flatMap((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); }); 

flatMap преобразует промежуточные Observables в один stream.

В последней версии flatMap оператор flatMap был переименован в mergeMap .

Я создал эту демонстрацию, чтобы выяснить, как обрабатывать токен обновления с помощью rxjs. Он делает это:

  • Делает вызов API с токеном доступа.
  • Если токен доступа истек (наблюдаемый вызывает соответствующую ошибку), он выполняет другой asynchronous вызов для обновления токена.
  • После обновления токена он повторит вызов API.
  • Если все еще ошибка, сдайтесь.

Эта демонстрация не делает настоящих HTTP-вызовов (имитирует их с помощью Observable.create ).

Вместо этого используйте его, чтобы узнать, как использовать функции catchError и retry для устранения проблемы (первый токен доступа был неудачным), а затем повторите неудачную операцию (вызов API).

  • Проверка ошибки с наблюдаемыми в сервисах
  • Как передавать данные между отдельными компонентами без использования $ scope?
  • Angular2: Как использовать несколько экземпляров одного и того же сервиса?
  • Тестирование. Невозможно разрешить все параметры для (ClassName)
  • Мин. / Макс. Валидатор в угловом 2 финале
  • Внедрение архитектуры плагинов / системы плагинов / сменных фреймов в Angular 2, 4, 5, 6
  • Как я могу наблюдать за изменениями в localStorage в Angular2?
  • Локальное хранение в Угловом 2
  • Угловая 2-функция Http, не отображающая map () и другие функции RxJS
  • $ http Auth Headers в AngularJS
  • Angular 2 2.0.0-rc.1 Свойство 'map' не существует в типе 'Observable ' не совпадает с сообщением о выпуске
  • Давайте будем гением компьютера.