简体   繁体   中英

Angular wait for method to finish

I have HTTP interceptor that intercepts http requests for api

First it check's is this api that requires authorization, after that checks for session variables to see is there token saved already and checks is it valid

If there is no token or token is expired, it should get new token and then carry on as usual

The rest of code gets executed before token is retrieved, and then it doesn't add required auth header. I don't know how to make it wait.

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    let authReq = request;
    const token = this.checkUrl(authReq.url)
    if (token){
      authReq = request.clone({ 
        headers: request.headers.set(this.TOKEN_HEADER_KEY, 'Bearer ' + token) 
      });
    }
    return next.handle(authReq);
  }

checkUrl method that gets token and checks if auth is required

checkUrl(url: string): string {
    if (url.includes('/login')){
      return ''
    }
    for (let x of this.includedList) {
      if (url.includes(x.url)){
        return this.auth.getApiToken()
        //break;
      }
    }
    return ''
  }

getApiToken method that gets token from session storage or from login endpoint

getApiToken(): string{
    let token = <string>sessionStorage.getItem('auth-token');
    if (!token || this.jwt.isTokenExpired(token)){
      this.apiLogin().subscribe({
        next: data => {
          console.log(data)
          sessionStorage.setItem("auth-token", data.token)
          token = data.token
        },
        error: err => {
          console.log(err)
          token = ''
        }
      })
    } 
    return token;
  }

And finally apiLogin method that gets new token from api

apiLogin(): Observable<any> {
    const loginData = {
      username: environment.apiLoginData.username,
      password: environment.apiLoginData.password
    }
    return this.http.post(AUTH_API, loginData);
  }

EDIT

Solution I tried with Observable and map

interceptor

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    let authReq = request;
    let token = ''
    this.checkUrlV2(authReq.url).pipe(map(data => {
      console.log(data)
      token = data
    }));
    console.log('token')
    console.log(token)
    if (token){
      authReq = request.clone({ 
        headers: request.headers.set(this.TOKEN_HEADER_KEY, 'Bearer ' + token) 
      });
    }
    return next.handle(authReq);
  }

checkUrlV2

checkUrlV2(url: string): Observable<string> {
    if (url.includes('/login')){
      return of('')
    }
    for (let x of this.includedList) {
      if (url.includes(x.url)){
        this.auth.apiTokenV2().pipe(map(data => {
          console.log(data)
          return of(data)
        }));
        //break;
      }
    }
    return of('')
  }

apiTokenV2

apiTokenV2() {
    let token = ''
    if (!token || this.jwt.isTokenExpired(token)){
      this.apiLogin().pipe(map(data => {
        console.log(data)
        sessionStorage.setItem("auth-token", data.token)
        return of(data.token)
      }));
    }
    return of(token)
  }

Found solution that works for my case. It does request without checking token, then refreshes it if it gets error

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = this.addAuthenticationToken(req);

    return <Observable<HttpEvent<any>>>next.handle(req).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error && error.status === 401) {
          if (this.refreshTokenInProgress) {
            // If refreshTokenInProgress is true, we will wait until refreshTokenSubject has a non-null value which means the new token is ready and we can retry the request again
            return this.refreshTokenSubject.pipe(
              filter(result => result !== null),
              take(1),
              switchMap(() => next.handle(this.addAuthenticationToken(req)))
            );
          } else {
            this.refreshTokenInProgress = true;
            // Set the refreshTokenSubject to null so that subsequent API calls will wait until the new token has been retrieved
            this.refreshTokenSubject.next(null);
            
            return this.refreshAccessToken().pipe(
              switchMap((success: boolean) => {               
                this.refreshTokenSubject.next(success);
                return next.handle(this.addAuthenticationToken(req));
              }),
              // When the call to refreshToken completes we reset the refreshTokenInProgress to false for the next time the token needs to be refreshed
              finalize(() => this.refreshTokenInProgress = false)
            );
          }
        } else {
          return throwError(() => new Error(error.message));
        }
      })
    );
  }

  private refreshAccessToken(): Observable<any> {
    return this.auth.apiLogin().pipe(map(authData => {
      console.log('Token refreshed')
      sessionStorage.setItem("auth-token", authData.token)
      return authData.token
    }))
  }
  private addAuthenticationToken(request: HttpRequest<any>): HttpRequest<any> {
    const token = <string>sessionStorage.getItem('auth-token')
    for (let x of this.excludedUrl){
      if (request.url.includes(x)){
        return request;
      }
    }
    for (let x of this.includedList) {
      if (request.url.includes(x.url)){
        console.log('Bearer token added')
        return request.clone({
          headers: request.headers.set(this.TOKEN_HEADER_KEY, 'Bearer ' + token) 
        });
      }
    }
    return request;
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM