【问题标题】:CanActivate Observable returning false when delay in service checkCanActivate Observable 在服务检查延迟时返回 false
【发布时间】:2021-03-02 13:25:46
【问题描述】:

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

问题:在我存在模拟数据的测试环境中,一切都按预期工作。检查运行,获取服务和价值。然后它根据该值返回真/假并调用相关逻辑。在使用真实服务的开发环境中,该值总是返回 undefined,因此总是触发 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 已实现,并使用ActivatedRouteSnapshot 从queryParam 中获取id。然后将其传递给validateId(queryParamsId) 方法以调用服务。一旦找到该值,它就会运行另一个方法validateIdBool(): boolean,以根据服务调用返回的值返回真/假。然后将 True 或 flase 返回到 canActivate,它返回 this.validateId(queryParamsId).pipe(map(e =&gt;... 并映射到 e。

【问题讨论】:

  • 这可能不起作用,因为您的return of(this.validateIdBool()) 将始终将this.isIdValid 作为undefined,考虑到订阅是您开发环境中的httpRequest。我建议你使用 RxJs 运算符之类的,我猜它的 switchMap。

标签: angular typescript rxjs angular-ui-router


【解决方案1】:

在这种情况下您必须使用 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);
  }
}

【讨论】:

  • 感谢您稍微清理一下代码!
【解决方案2】:

你可以从这个 sn-p 借一片叶子

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;
    }
  }
}

【讨论】:

    【解决方案3】:

    我修改了你的代码。

    此代码还将缓存已验证 ID 的结果。关键在于validateId 函数。当你有一个 observable 时,你必须转换它,而不是创建一个新的。如果您放置断点,您会看到断点的顺序以及断点的原因。

    这段代码会等待服务处理ID,取结果,看是否有效,如果抛出,则返回false。知道结果后,将其保存以备下次使用。

    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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-13
      • 1970-01-01
      • 1970-01-01
      • 2014-04-09
      • 1970-01-01
      相关资源
      最近更新 更多