[typescript] Angular2 canActivate () 호출 비동기 함수
Angular2 라우터 가드를 사용하여 내 앱의 일부 페이지에 대한 액세스를 제한하려고합니다. Firebase 인증을 사용하고 있습니다. 사용자가 중포 기지로 로그인되어 있는지 확인하기 위해, 나는 호출이 .subscribe()
온 FirebaseAuth
콜백와 객체입니다. 이것은 경비원의 코드입니다.
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AngularFireAuth } from "angularfire2/angularfire2";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Rx";
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AngularFireAuth, private router: Router) {}
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
this.auth.subscribe((auth) => {
if (auth) {
console.log('authenticated');
return true;
}
console.log('not authenticated');
this.router.navigateByUrl('/login');
return false;
});
}
}
가드가있는 페이지로 이동하면 authenticated
또는 not authenticated
콘솔에 인쇄됩니다 (Firebase의 응답을 기다리는 데 약간의 지연 후). 그러나 탐색은 완료되지 않습니다. 또한 로그인하지 않은 경우 /login
경로로 리디렉션됩니다 . 따라서 내가 가지고있는 문제 return true
는 요청한 페이지를 사용자에게 표시하지 않는다는 것입니다. 나는 이것이 콜백을 사용하고 있기 때문이라고 가정하고 있지만 그렇지 않으면 어떻게하는지 알 수 없습니다. 이견있는 사람?
답변
canActivate
Observable
완료 되는를 반환해야합니다 .
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AngularFireAuth, private router: Router) {}
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
return this.auth.map((auth) => {
if (auth) {
console.log('authenticated');
return true;
}
console.log('not authenticated');
this.router.navigateByUrl('/login');
return false;
}).first(); // this might not be necessary - ensure `first` is imported if you use it
}
}
있습니다 return
실종 내가 사용하는 map()
대신 subscribe()
때문에 subscribe()
반환 Subscription
이 아닌를Observable
답변
Observable
비동기 논리 부분을 처리하는 데 사용할 수 있습니다 . 예를 들어 테스트하는 코드는 다음과 같습니다.
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { DetailService } from './detail.service';
@Injectable()
export class DetailGuard implements CanActivate {
constructor(
private detailService: DetailService
) {}
public canActivate(): boolean|Observable<boolean> {
if (this.detailService.tempData) {
return true;
} else {
console.log('loading...');
return new Observable<boolean>((observer) => {
setTimeout(() => {
console.log('done!');
this.detailService.tempData = [1, 2, 3];
observer.next(true);
observer.complete();
}, 1000 * 5);
});
}
}
}
답변
canActivate
너무 Promise
해결하는 반환 할 수 boolean
있습니다
답변
약속으로 true | false를 반환 할 수 있습니다.
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
import {AuthService} from "../services/authorization.service";
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService:AuthService) { }
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return new Promise((resolve, reject) => {
this.authService.getAccessRights().then((response) => {
let result = <any>response;
let url = state.url.substr(1,state.url.length);
if(url == 'getDepartment'){
if(result.getDepartment){
resolve(true);
} else {
this.router.navigate(['login']);
resolve(false);
}
}
})
})
}
}
답변
가장 인기있는 답변을 확장합니다. AngularFire2 용 Auth API가 약간 변경되었습니다. AngularFire2 AuthGuard를 달성하기위한 새로운 서명입니다.
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
@Injectable()
export class AuthGuardService implements CanActivate {
constructor(
private auth: AngularFireAuth,
private router : Router
) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean>|boolean {
return this.auth.authState.map(User => {
return (User) ? true : false;
});
}
}
참고 : 이것은 매우 순진한 테스트입니다. User 인스턴스를 콘솔 로그하여 사용자의 더 자세한 측면에 대해 테스트 할 것인지 확인할 수 있습니다. 그러나 적어도 로그인하지 않은 사용자로부터 경로를 보호해야합니다.
답변
최신 버전의 AngularFire에서 다음 코드가 작동합니다 (최상 답변 관련). “파이프”방법의 사용법에 유의하십시오.
import { Injectable } from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {AngularFireAuth} from '@angular/fire/auth';
import {map} from 'rxjs/operators';
import {Observable} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthGuardService implements CanActivate {
constructor(private afAuth: AngularFireAuth, private router: Router) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return this.afAuth.authState.pipe(
map(user => {
if(user) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
})
);
}
}
답변
제 경우에는 응답 상태 오류에 따라 다른 동작을 처리해야했습니다. 이것이 RxJS 6+에서 작동하는 방식입니다.
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AngularFireAuth, private router: Router) {}
public canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | boolean {
return this.auth.pipe(
tap({
next: val => {
if (val) {
console.log(val, 'authenticated');
return of(true); // or if you want Observable replace true with of(true)
}
console.log(val, 'acces denied!');
return of(false); // or if you want Observable replace true with of(true)
},
error: error => {
let redirectRoute: string;
if (error.status === 401) {
redirectRoute = '/error/401';
this.router.navigateByUrl(redirectRoute);
} else if (error.status === 403) {
redirectRoute = '/error/403';
this.router.navigateByUrl(redirectRoute);
}
},
complete: () => console.log('completed!')
})
);
}
}
어떤 경우에는 적어도 operator 의 next
일부가 작동하지 않을 수 있습니다 . 그것을 제거하고 아래와 같이 오래된 상품을 추가하십시오 .tap
map
public canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | boolean {
return this.auth.pipe(
map((auth) => {
if (auth) {
console.log('authenticated');
return true;
}
console.log('not authenticated');
this.router.navigateByUrl('/login');
return false;
}),
tap({
error: error => {
let redirectRoute: string;
if (error.status === 401) {
redirectRoute = '/error/401';
this.router.navigateByUrl(redirectRoute);
} else if (error.status === 403) {
redirectRoute = '/error/403';
this.router.navigateByUrl(redirectRoute);
}
},
complete: () => console.log('completed!')
})
);
}