Can someone explain me how the interceptor catch e

2019-08-25 19:13发布

问题:

I've implemented my interceptor where I control error status, and I am confused about where should i catch errors or what should I use : .of, throw or empty ?

 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!req.url.includes('auth')) {
      console.log("NO INCLUDE auth ")
      let getTsObs = Observable.fromPromise(this.storage.get('token'));
      const tokenObservable = getTsObs.map(token => {
        return req = req.clone({
          setHeaders: {
            Authorization: `Bearer ${token}`
          }
        });
      });
      return tokenObservable.flatMap((req) => {
        return next.handle(req)
          .do((event: HttpEvent<any>) => {
            if (event instanceof HttpResponse) {
              // do stuff to the response here
            }
          }, error => {
            if (error.status === 403) {
              this.cleanApp();                  
              return Observable.throw(error);
            }
            else if(error.status === 404 ||error.status === 500){
              return Observable.of([]);
            }else {                
              return Observable.throw(error);                  
            }
          })
      })
    }
    else {
      console.log("INCLUDE auth")
      let clone = req.clone({
        setHeaders: {
          Accept: `application/json`,
          'Content-Type': `application/json`
        }
      });
      return next.handle(clone);
    }
  }

 private cleanApp() {  
    this._us.removeStorageTokenExpired();
    let nav = this.app.getActiveNav();
    nav.setRoot('Login');  
  }

I want to send to Login when error is 403 and cancel the next calls, for example if I am using forkjoin .

My backend return error 404 and 500 with result [], when this call doesnt return anything however I dont know if this is the best way to return that in case the call is ok but it doesnt return any row. For this reason you can see that my code return Observable.of([]) to it doesnt cancel forkjoins,for example.

Now my provider is :

public listGroups(action, search, page): Observable<any> {
    let url = URL_SERVICIOS + '/users/groups?action=' + action;
    if (action === "all") {
      url = url + '&search=' + search + '&page=' + page;
    }
    return this.http
      .get(url)
      .map(res => {
        if (action === "all") {
          return res;
        } else {
          return res['result'];
        }
      })
      .catch(error => {
        //if (error.status === 404||error.status===500)
          //return Observable.of([]);
        return Observable.of([]); 
      })
  }

The first time, when i uncommented 2 first lines i get this error :

ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

I've solved this error , commenting the 2 first lines.

Could someone explain me what is the best way to handle error with interceptor and providers correctly ?

On the other hand , I have a cleanApp to clean storage when error is 403, but if I have a forkjoin with 3 calls , it'll be call 3 times and it occurss an error. how can i achieve that to do that 1 time only .

Thank you

回答1:

I would simplify the interceptor, and handle errors with a Guard service. Also, I would avoid requesting the token from local storage. I would store it, pass it in memory, and retrieve it within an AuthService.

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(public _auth: AuthService) {}
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (this.auth.authToken) {
      console.log('JWT', this.auth.authToken)
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${this.auth.authToken}`,
        },
      })
    }
    return next.handle(request)
  }
}

Put this as a provider in your app.module

    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true,
    },
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private _router: Router,
    private _auth: AuthService
  ) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | boolean {
    const token = auth.authToken
    if (!token) {
      // Session Expired
      this._router.navigate(['/login'])
    }
    return true
  }
}

  {
    path: '',
    canActivate: [AuthGuard],
    component: LayoutMainComponent
  },
@Injectable()
export class AuthService {
  currentUser$: BehaviorSubject<any> = new BehaviorSubject<any>(null)

  public authToken: String

  constructor() {
    // save auth token
    this.currentUser$.pipe(filter(user => !!user)).subscribe(user => {
      this.authToken = user.token
    })