服务检查延迟时 CanActivate Observable 返回 false

CanActivate Observable returning false when delay in service check

上下文: 我在我的 Angular 应用程序上创建了一个路由守卫。当守卫在该路线上处于活动状态时,它会激活运行检查的守卫。在检查中,它调用服务以获取值。然后使用值映射 true/false。在 true 上它直接导航到路线,在 false 上它显示模态。这两种结果都与我面临的问题无关。

问题: 在我存在模拟数据的测试环境中,一切都按预期工作。检查运行,获取服务和价值。然后根据该值 returns true/false 并调用相关逻辑。在使用真实服务的开发环境中,该值总是 return 未定义,因此总是触发 false。这只发生在第一个实例中。我怀疑这是服务延迟和 true/false 逻辑 运行 在服务可以 return 之前。因此,我在我的模拟服务中添加了一个延迟,它复制了这个问题。

尝试修复: 我尝试在方法运行 true/false 逻辑之前将 .pipe(delay) 添加到方法中。我也试过从构造函数调用服务来提前调用它。

routeGuard.guard.ts

import { catchError, map } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { idService } from '../../idService.service';

@Injectable({
    providedIn: 'root'
})
export class UnauthorisedReportGuard implements CanActivate {
    constructor(private idService: idService) {}
isIdValid;
id;

validateId(queryParamsId): Observable<boolean> {
    this.idService.getId(queryParamsId).subscribe(selectedId => {
        this.id = selectedId;
        this.isIdValid = this.id.toLowerCase();
    });
    return of(this.validateIdBool());
}

validateIdBool(): boolean {
    if (this.isIdValid === 'validId') {
        return true;
    }
    return false;
}

canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    const queryParamsId = route.queryParams.id;

    return this.validateId(queryParamsId).pipe( //tried adding a delay here
        map(e => {
            // e returns false first time - this.isIdValid is undefined due to delay in getting service
            if (e) {
                return true;
            } else {
                //logic here if false
            }
        }),
        catchError(() => {
            return of(false);
        })
    );
}

}

代码解释:canActivate实现,使用ActivatedRouteSnapshotqueryParam获取id。然后将其传递给 validateId(queryParamsId) 方法以调用服务。一旦找到该值,它就会根据服务调用中的值 return 运行另一个方法 validateIdBool(): boolean 到 return true/false。然后将 True 或 flase returned 到 canActivate,其中 returns this.validateId(queryParamsId).pipe(map(e =>... 并映射到 e.

在这种情况下你必须使用switchMap,所以你在返回语句之前等待请求完成。在你的 routeGuard.guard.ts:

上尝试这样的事情
@Injectable({
  providedIn: 'root',
})
export class UnauthorisedReportGuard implements CanActivate {
  constructor(private idService: idService) {}
  isIdValid;
  id;

  validateId(queryParamsId): Observable<boolean> {
    return this.idService.getId(queryParamsId).pipe(
      switchMap((request) => {
        this.id = request.id;
        this.isIdValid = this.id.toLowerCase();
        return of(this.validateIdBool(request.id.toLowerCase())); // parameter to avoid mutable variables.
      }),
      catchError(() => of(false))
    );
  }

  validateIdBool(id): boolean {
    return id === 'validId';
  }

  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    const queryParamsId = route.queryParams.id;
    return this.validateId(queryParamsId);
  }
}

我已经修改了你的代码。

此代码还将缓存已验证 ID 的结果。关键在 validateId 函数中。当您拥有一个可观察对象时,您必须对其进行转换,而不是创建一个新对象。如果放置断点,您会看到断点的顺序以及断点的原因。

这段代码会等待服务处理ID,取结果,看是否有效,如果抛出,就returnsfalse。知道结果后,它会保存起来,以备下次使用。

import { catchError, map } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { idService } from '../../idService.service';

@Injectable({
    providedIn: 'root'
})
export class UnauthorisedReportGuard implements CanActivate {
    idValidity: {
        [key: string]: Observable<boolean>;
    } = {};

    constructor(private idService: idService) {}

validateId(queryParamsId): Observable<boolean> {
    if (!this.idValidity[queryParamsId]) {
        this.idValidity[queryParamsId] = this.idService.getId(queryParamsId).pipe(
            map(selectedId => this.validateIdBool(selectedId)), // Transform service result
            catchError(() => of(false)), // In case of error, return false
        ); // Save it for next use
    }

    return this.idValidity[queryParamsId];
}

// If this function is this simple, you can inline it into the `map` function
validateIdBool(selectedId: string): boolean {
    if (selectedId === 'validId') {
        return true;
    }
    return false;
}

canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    return this.validateId(route.queryParams.id);
}

你可以从这个片段中借一片叶子

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';


@Injectable({
  providedIn: 'root'
})
export class AdminAuthGuardGuard implements CanActivate {
  
  constructor( public _auth:AuthService, public _router:Router ) {

  }
  canActivate():boolean {
    if(this._auth.loggedIn()){
      return true;
    }
    else{
      this._router.navigate(['/login']);
      return false;
    }
  }
}