angular 2 http exception handler and jwt refresh

一世执手 提交于 2019-12-08 05:53:41

问题


I am trying to refresh a JWT token when I got a certain exception, when it is another exception my ErrorHandler should handle them.

I have one piece of code, one where the token refresh works, and one piece of code where the exception handler works, but I just can't combine them in a working way.

The problem is that I can't throw an exception and catch it with my ErrorHandler in an observable.

Here is the code where I can refresh my token with. When it fails it checks if the error code is token_expired, when it is it will refresh the token and retry the request.

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});
  }
}

The only bad thing about the above is that I can't catch the exception in my exception handler.

The following code can throw exceptions that can be catched by the ErrorHandler. But I have no idea how I can refresh the token in one call...

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});
  }
}

My error handler is only contains a console.log().
https://angular.io/api/core/ErrorHandler

How can I get this working?


回答1:


After some hours I finally got the solution!

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);
    });
  }

}


来源:https://stackoverflow.com/questions/45693820/angular-2-http-exception-handler-and-jwt-refresh

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!