Angular 7 RXJS Observables计时器间隔无限循环立即启动

Angular 7 RXJS Observables timer interval infinite loop start immediately

我正在尝试使这些RXJS可观对象在Angular 7中正常工作,但我不知道出了什么问题。

我想做的是检查每10秒是否有用于验证的验证令牌状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {interval, Observable, of, timer} from 'rxjs';
import {concatMap, delay, flatMap, takeWhile, tap} from 'rxjs/operators';
import {Token} from './Token';
import {User} from './User';
import {TokenFactory} from './factory/tokenfactory';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable({
  providedIn: 'root'
})
export class TokenService {
  private user;
  private token;
  private tokenLoginUrl = 'http://localhost:8080/login_check';
  private refreshTokenUrl = 'http://localhost:8080/token/refresh';

  constructor(private http: HttpClient) { }

  private verifyTokenState(): Observable {
    if (!localStorage.getItem('token')) {
      this.getLoginInfo().subscribe(token => {
        this.token = TokenFactory.createToken(token.token, token.refresh_token, Date.now(), true);
        return of(this.token);
      });
      this.token = {};
      return of(this.token);
    }

    this.token = TokenFactory.getTokenFromLocalStorage();
    const timeElapsedInMinutes = (Date.now() - this.token.date_token) / 1000 / 60;
    console.log(timeElapsedInMinutes);

    if (timeElapsedInMinutes >= 59) {
      this.getRefreshToken(this.token).subscribe(jsonToken => {
        this.token = TokenFactory.createToken(jsonToken.token, jsonToken.refresh_token, Date.now(), true);
        return of(this.token);
      });
    }
    return of(this.token);
  }

  private getLoginInfo(): Observable {
    return this.http.post<Token>(this.tokenLoginUrl, this.user, httpOptions);
  }

  private getRefreshToken(token: Token): Observable {
    return this.http.post<Token>(this.refreshTokenUrl, token, httpOptions);
  }

  public getToken() {
    return timer(0, 10000).pipe(
        flatMap(() => this.verifyTokenState()),
        takeWhile(() => this.token !== undefined && !this.token.isValid)
    );
  }

  public setUser(user: User): void {
    this.user = user;
  }
}

我遇到的问题是,当我从另一个组件内部调用get TokenService.getToken()时(假设用户单击提交按钮后在登录组件中),并且我使用timer(0,10000) getLoginInfo()和
getRefreshToken()方法在无限循环中被调用,并且触发了数百个ajax调用。

1
2
3
4
5
6
public getToken() {
    return timer(0, 10000).pipe(
        flatMap(() => this.verifyTokenState()),
        takeWhile(() => this.token !== undefined && !this.token.isValid)
    );
  }

我不明白的是,如果我使用2000而不是0作为第一个参数,则一切正常,但这不是我想要的。

1
2
3
4
5
6
 public getToken() {
        return timer(2000, 10000).pipe(
            flatMap(() => this.verifyTokenState()),
            takeWhile(() => this.token !== undefined && !this.token.isValid)
        );
      }

我确切想要的是观察到的呼叫之间有10秒的间隔,但我希望它立即开始。我还想获得从verifyTokenSate()方法返回的getToken()返回的可观察令牌。那么到底是怎么了?有更好的方法吗?我想以被动方式进行此操作,而无需取消订阅。

经过进一步调查后,该问题似乎在AuthInterceptor中发生。当我从模块提供程序中删除时,一切正常。这是AuthInterceptor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import {Injectable} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {TokenService} from './token.service';
import {Observable} from 'rxjs';
import {map, mergeMap} from 'rxjs/operators';


@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    private token;
    constructor(private auth: TokenService) { }

    intercept(
        req: HttpRequest,
        next: HttpHandler
    ): Observable<HttpEvent> {
        let request = req.clone();
        this.auth.getToken().subscribe(t => {
            console.log(t);
            if (t.token) {
                console.log('token valid');
                request = req.clone({
                    setHeaders: { Authorization: `Bearer ${t.token}` }
                });
            }
        });

        return next.handle(request);
    }
}

那么AuthInterceptor内部有什么问题?


AuthInterceptor可能会拦截http登录/刷新URL的请求,进而本身(通过this.auth.getToken())触发这些调用,从而导致循环性。 一种可能的解决方案是不拦截(或跳过)这些http请求

1
2
3
4
5
6
7
8
9
10
11
12
intercept(
    req: HttpRequest,
    next: HttpHandler
): Observable<HttpEvent> {
    // whitelisting certain urls that don't need token check
    if (req.url.includes('/login_check')
        || req.url.includes('/token/refresh')) {
        return next.handle(req);
    }

    // remaining code
}