canActivate не работает при вводе URL
В основном, все работает правильно, пока по какой-то причине, когда в URL-адресе не будет введена переменная this.estado в методе canActivate, не окажется неопределенным.
Я думаю, что это потому, что конструктор не получает наблюдаемого в нужное время.
import { Component, Injectable, Inject, OnInit } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
@Injectable()
export class AuthService implements CanActivate {
myAppUrl: string;
estado: any;
constructor(private router: Router, private http: HttpClient, @Inject('BASE_URL') private baseUrl: string) {
this.myAppUrl = baseUrl;
this.estadoSetUp(); /* If I put hear this.estado = true everything works fine */
}
public getJSON(): Observable<any> {
return this.http.get(this.myAppUrl + 'api/IsAuthenticated');
}
public estadoSetUp() {
this.getJSON().subscribe(data => {
this.estado = data.AmILoggin;
});
}
canActivate(): Observable<boolean> {
if (this.estado != true) {
this.router.navigate(['/']);
}
return this.estado;
}
}
Решено благодаря @sirdieter
Я оставляю здесь решение для тех, у кого будут проблемы в будущем:
import { Component, Injectable, Inject, OnInit } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject'
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/do';
@Injectable()
export class AuthService implements CanActivate {
myAppUrl: string;
private isAuthorized = new ReplaySubject<boolean>(1);
constructor(private router: Router, private http: HttpClient, @Inject('BASE_URL') private baseUrl: string) {
this.myAppUrl = baseUrl;
this.estadoSetUp();
}
public getJSON(): Observable<any> {
return this.http.get(this.myAppUrl + 'api/IsAuthenticated');
}
public estadoSetUp() {
this.getJSON().subscribe(data => {
this.isAuthorized.next(data.AmILoggin);
});
}
canActivate(): Observable<boolean> {
return this.isAuthorized.asObservable()
.do(auth => {
if (auth != true) {
this.router.navigate(['/']);
}
});
}
}
2 ответа
Вы правильно обнаружили проблему. Вы можете вернуть наблюдаемый http-вызов и сопоставить его со свойством AmILoggin. (RxJS 6 Синтаксис, кажется, у вас есть 5)
canActivate(): Observable<boolean> {
return this.getJSON().pipe(pluck('AmILoggin');
}
Но в этом случае вы будете делать вызов http каждый раз, когда вызывается canActivate(); Чтобы иметь только один http-вызов, вы можете использовать тему:
private isAuthorized = new ReplaySubject<boolean>(1);
public estadoSetup() {
this.getJSON().subscribe(data => this.isAuthorized.next(data.AmILoggin));
}
// RxJS 6
canActivate() {
return this.isAuthorized.asObservable().pipe(
tap(auth => {
if (auth) {
this.router.navigate(['/']);
}
}
);
}
// RxJS 5
canActivate(): Observable<boolean>
return this.isAuthorized.asObservable()
.do(auth => {
if (auth) {
this.router.navigate(['/']);
}
});
Помните, что ваш http-вызов асинхронный,
Поэтому, когда вы открываете URL-адрес напрямую, создается эта защита (она запускает вызов http), и очень скоро после этого (миллисекунды) вызывается canActivate. В результате, если ваш http-вызов не был достаточно быстрым, ваша переменная не определена, потому что нет никаких результатов от http-вызова.
canActivate возвращает Observable логического значения. Таким образом, одним из решений было бы изменить ваше переменное состояние на наблюдаемое