Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 7 Error Interceptor – Problem with original call

Angular 7 Error Interceptor – Problem with original call I've written an interceptor to catch all 401 errors. If such an error occurs it should try to get a new JWT token and if that worked repeat the original request. So far, the interceptor works. My problem is, that the orignal request after I gave it the new token doesn't end up in the subscribe again which was attached to the original observable.

Original Request

Component:

this.serviceModel.getSearchItems(si).subscribe(res => {
   this.resultData = res;
});

ServiceModel

public getSearchItems(si: SearchInfo): Observable<VoList<SearchResultItemVO>> {
   return this.post<VoList<SearchResultItemVO>>(`/api/Search/GetSearchItems`, si, null,  SearchResultItemVO);
}

Intercepter

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  constructor(private serviceModel: SharedAccountServiceModel) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).catch(err => {
      return this.handleError(err, request, next);
    });
  }
  handleError(err, request: HttpRequest<any>, next: HttpHandler) {
    if (err.status === 401) {
      if (!!localStorage.getItem('auth_token') &&     !!localStorage.getItem('refresh_token')) {
        this.serviceModel.refreshlogin().switchMap(res => {
          return next.handle(this.addAuthenticationToken(request));
    })
    .subscribe();
  }
  else {
    localStorage.removeItem('accessinfo');
    localStorage.removeItem('auth_token');
    localStorage.removeItem('userid');
    location.reload(true);
  }
}
const error = err.error.message || err.statusText;
return throwError(error);
  }
  addAuthenticationToken(request) {
const accessToken = localStorage.getItem('auth_token');
if (!accessToken) 
  return request;    
return request.clone({
  setHeaders: {
    Authorization: `Bearer ${localStorage.getItem('auth_token')}`
  }
    });
  }
}

From my understanding switchMap should contribute to the original subscribe being executed again, but it doesn't work. The call is executed but it does not arrive in the subscribe.

like image 208
michael Avatar asked Mar 04 '23 18:03

michael


1 Answers

I'm facing pretty close problem and I solve that with a 'switchmap' in a 'pipe', with HttpInterceptor like this :

HttpErrorFilter :

import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { catchError, filter, finalize, switchMap, take } from 'rxjs/operators';

import { AuthenticationService } from './../services/authentication.service';
import { Injectable } from '@angular/core';

@Injectable()
export class HttpErrorFilter implements HttpInterceptor {

    isRefreshingToken = false;
    tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);

    constructor(private authenticationService: AuthenticationService) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(
            catchError((error: any) => {
                if (error.status === 401 && !request.url.includes('auth/signin')) {
                    return this.handle401Error(request, next);
                }
                return throwError(error);
            })
        );
    }

    private handle401Error(request: HttpRequest<any>, next: HttpHandler) {

        if (request.url.includes('auth/refreshtoken')) {
            this.isRefreshingToken = false;
            return of(<any>this.authenticationService.logout());
        }
        if (!this.isRefreshingToken) {
            this.isRefreshingToken = true;
            this.tokenSubject.next(null);

            return this.authenticationService.refresh().pipe(switchMap(token => {
                if (token) {
                    this.tokenSubject.next(token.value);
                    return next.handle(request);
                }
                return of(<any>this.authenticationService.logout());
            }),
            catchError(err => {
                this.authenticationService.logout();
                return throwError(err.error);
            }),
            finalize(() => {
                this.isRefreshingToken = false;
            }));
        } else {
            this.isRefreshingToken = false;

            return this.tokenSubject
                .pipe(filter(token => token != null),
                take(1),
                switchMap(token => {
                    return next.handle(request);
                }));
        }
    }
}

HttpFilter :

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from './../../../environments/environment';

@Injectable()
export class HttpFilter implements HttpInterceptor {

    private apiUrl = environment.apiUrl;

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(this.addAuthentication(request));
    }

    private addAuthentication(request: HttpRequest<any>): HttpRequest<any> {

        if (!request.url.includes('/auth/')) {
            const token =  localStorage.getItem('access.token');
            if (token) {
                request = request.clone({
                    setHeaders: {Authorization: 'Bearer ' + token}
                });
            }
        }
        return request.clone({url: `${this.apiUrl}${request.url}`});

    }

}

NgModule provides :

@NgModule({
...
providers: [
    { provide: HTTP_INTERCEPTORS, useClass: HttpErrorFilter, multi: true },
    { provide: HTTP_INTERCEPTORS, useClass: HttpFilter, multi: true },
    ...
  ],
...
})
like image 55
Julien Berger Avatar answered Mar 31 '23 16:03

Julien Berger