Cét*_*tia 7 javascript rxjs firebase typescript angular
我的身份验证基于两件事:
我还有基于isAuthenticated()返回Observable的authGuards (因为在页面刷新时,guard必须等待auth完成才能将用户重定向到任何地方).
问题:我无法找到一种方法来使所有async和rxjs混乱/地狱工作..目前它正在工作,但每次isAuthenticated调用,每次都调用serverAPI auth ...我怎么能按顺序重构只调用一次服务器,所有的异步/重载东西仍然有效吗?
AuthService:
export class AuthService {
public userRole: UserBoRole;
public authState$: Observable<firebase.User>;
constructor(
private afAuth: AngularFireAuth,
private snackBar: SnackBarService,
private translate: TranslateService,
private router: Router,
private grpcService: GrpcService
) {
this.authState$ = this.afAuth.authState.pipe(
take(1),
mergeMap(user => {
if (!user) {
return of(user);
}
// User is successfully logged in,
// now we need to check if he has a correct role to access our app
// if an error occured, consider our user has not logged in, so we return null
return this.checkProfile().pipe(
take(1),
map(() => {
this.test = true;
return user;
}),
catchError(err => {
console.error(err);
return of(null);
})
);
})
);
// Subscribing to auth state change. (useless here because access logic is handled by the AuthGuard)
this.authState$.subscribe(user => {
console.log('authState$ changed :', user ? user.toJSON() : 'not logged in');
});
}
checkProfile() {
return this.callAuthApi().pipe(
map((customer) => {
if (!customer || customer.hasRole() === "anonymous") {
return Promise.reject(new Error(AuthService.AUTH_ERROR_ROLE));
}
this.userRole = customer.getRole();
})
);
}
isAuthenticated(): Observable<boolean> {
return this.authState$.pipe(map(authState => !!authState));
}
}
Run Code Online (Sandbox Code Playgroud)
AuthGuard:
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private authService: AuthService, private router: Router) {}
check(): Observable<boolean> {
return this.authService.isAuthenticated().pipe(
catchError(err => {
// notifying UI of the error
this.authService.handleAuthError(err);
// signout user
this.authService.signOut();
// if an error occured, consider our user has not logged in
return of(false);
}),
tap(isAuthenticated => {
if (!isAuthenticated) {
// redirecting to login
this.router.navigate(['login']);
}
})
);
}
canActivateChild(): Observable<boolean> {
return this.check();
}
canActivate(): Observable<boolean> {
return this.check();
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢
哈哈,ReactiveX 并不容易。它有一个相当陡峭的学习曲线。但它确实很强大。
1.只调用一次服务器
您可以使用共享重播。
要了解 shareReplay 的工作原理,请查看此处https://ng-rxjs-share-replay.stackblitz.io
//shareReplay example
ngOnInit() {
const tods$ = this.getTodos();
tods$.subscribe(console.log);// 1st sub
tods$.subscribe(console.log);// 2st sub
}
getTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>(this.url)
.pipe(
tap(() => console.log('Request')),
shareReplay(1) // compare with comment and uncomment
);
}
Run Code Online (Sandbox Code Playgroud)
使用 shareReplay 输出
Request
[Object, Object, Object]
[Object, Object, Object]
Run Code Online (Sandbox Code Playgroud)
不带 shareReplay 的输出
Request
[Object, Object, Object]
Request
[Object, Object, Object]
Run Code Online (Sandbox Code Playgroud)
您可以在您的身份验证服务代码中使用 shareReplay。
//auth.services.ts
import { shareReplay } from 'rxjs/operators';
...
this.user$ = this.afAuth.authState.pipe(
tap(user => {
console.log('login user$ here', user)
}),
switchMap(user => {
if (user) {
//do something
return this.db.object(`users/${user.uid}`).valueChanges();
} else {
return of(null);
}
}),
shareReplay(1) //**** this will prevent unnecessary request****
);
Run Code Online (Sandbox Code Playgroud)
2. 异步和等待
toPromise()
//auth.service.ts
...
getUser() {
return this.user$.pipe(first()).toPromise();
}
//auth.guard.ts
...
async canActivate(next: ActivatedRouteSnapshot
, state: RouterStateSnapshot
): Promise<boolean> {
const user = await this.auth.getUser();
//TODO your API code or other conditional authentication here
if (!user) {
this.router.navigate(['/login']);
}
return !!user;
}
Run Code Online (Sandbox Code Playgroud)
希望对你有帮助。
| 归档时间: |
|
| 查看次数: |
155 次 |
| 最近记录: |