Rxjs/Angular 4- 如果我在地址栏上输入 AuthGuarded URL,在 Auth 状态检查中使用 publishReplay() 会使我的应用程序无响应

Rxjs/Angular 4- using publishReplay() on Auth status checking makes my app unresponsive if I enter an AuthGuarded URL on the address bar

首先,我的没有 publishReplay() 的应用程序现在工作正常,但我想通过使用某种缓存来优化请求,以避免每次都去后端检查用户是否登录。我有以下上下文。我有需要根据是否登录的用户隐藏或显示的身份验证感知组件。我有

a) 一个 AuthGuard 来保护某些路由,如果用户未登录,它会将用户重定向到登录路由:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs/Observable';


@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router){}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
     return this.authService.isAuthenticated().map(isAuth => {
       console.log('is authenticated',isAuth);
            if (isAuth) {
                return true;
            }else{
                this.router.navigate(['/signin']);
                return false;
            }
        });
  }
}

b) 使用方法 isAuthenticated 的 AuthService 以检查用户是否已登录(我正在使用存储在数据库中的常规会话)。在链的末尾,我使用的是 publishReplay(3),因为我有 3 个组件在加载时可以识别身份验证。请注意,如果我删除该方法,一切正常并且 authguard 完成其工作,如果我添加它然后我转到示例 localhost:4200/dashboard 并且应用程序冻结,链接不起作用并且 auth guard 代码是没有得到执行,因为我在 AuthGuard 上放置了一个 console.log 'is authenticated' (如上所示)并且它没有显示在控制台上,给人的想法是执行永远不会到达那里。如果我删除 publishReplay,那么我会在控制台上再次看到消息:

isAuthenticated():可观察{

  let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
  return this.http.get('http://localhost:3000/api/addsfiliates/sponsor/check/login',options)
    .map(response => {
      let res = response.json();
      console.log("response");
      if (res.code == 200) {
        this.userIsAuthenticated.next(true);
        return true;
      }
    }
  ).catch((err)=>{
    //maybe add in the future if the code is 403 then send him to login otherwise send him elsewhere
    return Observable.of(false);
  }).publishReplay(3);

}

c)保护路由的路由文件,仅作上下文解释:

const appRoutes: Routes = [
  { path: '', redirectTo: '/', pathMatch:'full'},
  { path: '', component: MainComponent },
  { path: 'signin', component:SigninComponent},
  { path: 'signup', component: SignupComponent},
  { path: 'dashboard', canActivate:[AuthGuard],component: DashboardComponent,
    children: [
      { path: '', redirectTo:'dashboard/overview', pathMatch: 'full'},
      { path: 'overview', component: OverviewCampaignsComponent },
      { path: 'active', component: ActiveCampaignsComponent},
      { path: 'history', component: HistoryCampaignsComponent}
    ] }

]

我是否采用了正确的方法来对授权感知组件进行缓存?如果是,我如何使用此发布重放方法使其适用于我的用例?非常感谢

publishReplay returns ConnectableObservable 你需要调用 connect 然后它会连接到源 observable:

let obs = this.http.get(...)
    .publishReplay(1);
obs.connect;
return obs;

这应该可以修复您的应用,但您无法完成您想要的。因为在每次调用时,您仍然会创建一个新的可观察对象,从而调用服务器。

最简单直接的解决方案是:

export class AuthService {
    private isAuthenticatedValue: boolean;

    isAuthenticated(): Observable<boolean> {
        if(this.isAuthenticatedValue != null) {
            return Observable.of(this.isAuthenticatedValue);
        }

        return this.http.get(...)
            ...
            .do(flag => {
                this.isAuthenticatedValue = flag;
            })
    }
}

此外,您应该定期清除缓存值,因为会话可能会在服务器上过期。您可以通过使用 setTimeout.

清除值来实现