服务检查延迟时 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
实现,使用ActivatedRouteSnapshot
从queryParam
获取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;
}
}
}
上下文: 我在我的 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
实现,使用ActivatedRouteSnapshot
从queryParam
获取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;
}
}
}