обработчик исключений angular 2 http и обновление jwt

Я пытаюсь обновить токен JWT, когда у меня есть определенное исключение, когда это другое исключение, мой ErrorHandler должен их обрабатывать.

У меня есть один фрагмент кода, в котором работает обновление маркера, и один фрагмент кода, в котором работает обработчик исключений, но я просто не могу объединить их в рабочем порядке.

Проблема в том, что я не могу создать исключение и поймать его с помощью моего ErrorHandler в наблюдаемом объекте.

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

export class HttpErrorService extends Http {

  constructor(backend: XHRBackend, defaultOptions: RequestOptions) {
    super(backend, defaultOptions);
  }

  request(url: string | Request, options?: RequestOptionsArgs, disableRefresh = false): Observable<Response> {
      return super.request(url, options).catch((error: Response) => {
        // Refresh token on token_expired exception.
        if (!disableRefresh && error.status === 401 && error.json().error.code === 'token_expired') {
          return this.renewToken().flatMap((response) => {
            const res = response.json();
            // Replace the token in storage.
            localStorage.setItem('__token', res.data.token);

            // Replace request the token with the new one.
            if (url instanceof Request) {
              url.headers.set('Authorization', 'Bearer ' + res.data.token);
            } else if (options) {
              options.headers.set('Authorization', 'Bearer ' + res.data.token);
            }

            // To prevent a loop disable refreshing at the next request.
            return this.request(url, options, true);
          });
        }

        // Here I want to throw the exception.
        // I need to be able to catch it with my exception handler.
        // throw error; doesn't work.
        return Observable.throw(error);
      });
  }

  private getBaseUrl(): string {
    return environment.base_uri;
  };

  renewToken(): Observable<Response> {
    const headers = new Headers();
    headers.append('Authorization', 'Bearer ' + localStorage.getItem('__token'))

    return this.post(this.getBaseUrl() + '/auth/refresh', {}, {headers: headers});
  }
}

Единственная плохая вещь в вышеизложенном заключается в том, что я не могу поймать исключение в своем обработчике исключений.

Следующий код может генерировать исключения, которые могут быть перехвачены ErrorHandler. Но я понятия не имею, как я могу обновить токен за один вызов...

export class HttpErrorService extends Http {

  constructor(backend: XHRBackend, defaultOptions: RequestOptions) {
    super(backend, defaultOptions);
  }

  request(url: string | Request, options?: RequestOptionsArgs, disableRefresh = false): Observable<Response> {
    return Observable.create(observer => {
      super.request(url, options).subscribe(
        res => observer.next(res),
        err => {
          if (!disableRefresh && err.status === 401 && err.json().error.code === 'token_expired') {
            // I can't return this.renewToken()...
          }
          observer.error(err);
          throw new HttpException(err); // this is getting catched by the ErrorHandler
        },
        () => observer.complete);
    });
  }

  private getBaseUrl(): string {
    return environment.base_uri;
  };

  renewToken(): Observable<Response> {
    const headers = new Headers();
    headers.append('Authorization', 'Bearer ' + localStorage.getItem('__token'))

    return this.post(this.getBaseUrl() + '/auth/refresh', {}, {headers: headers});
  }
}

Мой обработчик ошибок содержит только console.log().
https://angular.io/api/core/ErrorHandler

Как я могу заставить это работать?


person Jan Wytze    schedule 15.08.2017    source источник
comment
Можете еще добавить код, где собственно вызывается функция запроса и ErrorHandler?   -  person trungk18    schedule 15.08.2017
comment
@ trungk18 Я переопределяю класс Http, поэтому каждый HTTP-запрос использует функцию запроса. Это библиотека HTTP по умолчанию. ErrorHandler — это всего лишь console.log();   -  person Jan Wytze    schedule 15.08.2017
comment
Привет, Ян, можешь попробовать бросить Observable.throw(ошибка) вместо возврата Observable.throw(ошибка) в свой первый блок кода?   -  person trungk18    schedule 15.08.2017
comment
@ trungk18 trungk18 Он достигает ошибки подписки, но не достигает обработчика исключений, а мой второй фрагмент кода достигает их обоих. Я использую ошибку подписки для отображения сообщений об ошибках пользователю. И обработчик исключений для обработки ошибок.   -  person Jan Wytze    schedule 15.08.2017
comment
Я не очень хорошо разбираюсь в rxjs, поэтому нам может понадобиться совет эксперта: D   -  person trungk18    schedule 17.08.2017
comment
@ trungk18 Я думаю, что неправильно использую исключения. Я выбрасываю одно и то же исключение дважды. Один попадает в обработчик исключений, а другой — в ошибку подписки. Сначала это сработало, но не с тех пор, как я исправил обновление токена, оно больше не работает. Я думаю, что мой дизайн обработки исключений просто плох...   -  person Jan Wytze    schedule 17.08.2017


Ответы (1)


Через несколько часов я наконец получил решение!

export class HttpErrorService extends Http {

  constructor(backend: XHRBackend, defaultOptions: RequestOptions) {
    super(backend, defaultOptions);
  }

  request(url: string | Request, options?: RequestOptionsArgs, disableRefresh = false): Observable<Response> {
    return Observable.create(observer => {
      super.request(url, options).retryWhen(attempts => this.retryRequest(attempts)).catch((error: Response) => {
        // Refresh token on token_expired exception.
        if (!disableRefresh && error.status === 401 && error.json().error.code === 'token_expired') {
          return this.renewToken().flatMap((response) => {
            const res = response.json();
            // Replace the token in storage.
            localStorage.setItem('__token', res.data.token);

            // Replace request the token with the new one.
            if (url instanceof Request) {
              url.headers.set('Authorization', 'Bearer ' + res.data.token);
            } else if (options) {
              options.headers.set('Authorization', 'Bearer ' + res.data.token);
            }

            // To prevent a loop disable refreshing at the next request.
            return this.request(url, options, true);
          });
        }

        throw Observable.throw(error);
      }).subscribe(
        res => observer.next(res),
        err => {
          observer.error(err);
          throw new HttpException(err);
        }
      );
    });
  }

  private getBaseUrl(): string {
    return environment.base_uri;
  };

  renewToken(): Observable<Response> {
    const headers = new Headers();
    headers.append('Authorization', 'Bearer ' + localStorage.getItem('__token'))

    return this.post(this.getBaseUrl() + '/auth/refresh', {}, {headers: headers});
  }

  retryRequest(attempts: any) {
    let count = 0;

    return attempts.flatMap(error => {
        return ++count >= 3 ? Observable.throw(error) : Observable.timer(count * 1000);
    });
  }

}
person Jan Wytze    schedule 18.08.2017