Angular 10 行为主体 returns 调用 .next(value) 方法后为空
Angular 10 behavior subject returns null after calling .next(value) method
我一直在绞尽脑汁想弄清楚是什么原因导致登录后我的用户值为空。
这是我的登录页面。它调用 api 并得到如下所示的结果:
这是我的身份验证服务:
export class AuthenticationService {
private userSubject: BehaviorSubject<User>;
public user: Observable<User>;
constructor(
private router: Router,
private http: HttpClient
) {
this.userSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('user')));
this.user = this.userSubject.asObservable();
}
public get userValue(): User {
return this.userSubject.value;
}
GetUsername(): string {
if (localStorage.getItem('currentUser') != null)
return JSON.parse(localStorage.getItem('currentUser')).UserName;
return null;
}
login(username: string, password: string) {
return this.http.post<any>(`${Statics.ApiUrl}users/authenticate`, { Username: username, Password: password }, { responseType: "json" })
.pipe(map(user => {
debugger;
localStorage.setItem('user', JSON.stringify(user));
this.userSubject.next(user);
return user;
}));
}
logout() {
//remove user from local storage to log user out
localStorage.removeItem('user');
this.userSubject.next(null);
this.router.navigate(['/login']);
}
signUp(RegisterModel: RegisterModel): Observable<User> {
return this.http.post<User>(`${Statics.ApiUrl}users/signup`, RegisterModel, { responseType: "json" });
}
verifyUserPhone(username: string, password: string, verificationCode: string): Observable<User> {
return this.http.put<User>(Statics.ApiUrl + 'users/verifyUserPhone', { username, password, verificationCode }, { responseType: "json" });
}
}
这是我的授权人:
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private authenticationService: AuthenticationService
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
debugger;
const user = this.authenticationService.userValue;
var token_expiration=new Date(user?.TokenExpirartion);
var now=new Date();
if (user && token_expiration>now) {
// check if route is restricted by role
if (route.data.roles && route.data.roles.indexOf(user.role) === -1) {
// role not authorised so redirect to home page
this.router.navigate(['/']);
return false;
}
// authorized so return true
return true;
}
// not logged in so redirect to login page with the return url
this.authenticationService.logout();
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
return false;
}
}
我登录并获取用户数据并将其存储在本地存储中,但我的 authguard 从不激活路由,因为身份验证服务总是 return 用户 NULL.If 然后我只刷新页面有用。至于注销,它不会注销用户,直到我刷新页面。
见下文:
这是我的登录按钮 ts 代码:
this.AuthenticationService.login(this.username, this.password)
.pipe(first())
.subscribe({
next: () => {
// get return url from query parameters or default to home page
const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
this.router.navigateByUrl(returnUrl);
},
error: error => {
this.error = error;
this.submited = false;
}
});
当 authguard 在登录后检查 userSubject 时 returns null。无论我登录多少次,除非我刷新页面,否则它将 return 为空。
只有这样 userSubject 才会被评估。我不知道该怎么办。我无法登录我的网站。我必须登录然后刷新页面才能使 authguard 工作。
刷新页面后我得到值:
似乎授权服务需要一些时间来获取数据,到那时路由已被拒绝,因为它为空。
您可以尝试在 approving/rejecting 路线
之前添加一些等待
你们的服务是在 root 中提供的吗?如果没有,您可能会得到多个实例。
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
...
这里的问题是您的 authguard 是同步的,并且在实际加载用户之前调用了 canActivate。如果您的 canActivate 将 return observable of boolean,而不是 boolean,您可以解决此问题。
首先,您需要添加函数,如果已经加载,用户将 return 可观察到该函数,并加载用户观察,如下所示:
public getOrFetchUser(): Observable<User> {
// please note that we return observable in both cases
return this.userSubject.value ? of(this.userSubject.value) : this.login();
}
然后将您的 canActivate 守卫更改为:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.authenticationService.getOrFetchUser().pipe(
map((user) => {
console.log('user, ', user);
// change return to your validation logic
return true;
});
我一直在绞尽脑汁想弄清楚是什么原因导致登录后我的用户值为空。
这是我的登录页面。它调用 api 并得到如下所示的结果:
这是我的身份验证服务:
export class AuthenticationService {
private userSubject: BehaviorSubject<User>;
public user: Observable<User>;
constructor(
private router: Router,
private http: HttpClient
) {
this.userSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('user')));
this.user = this.userSubject.asObservable();
}
public get userValue(): User {
return this.userSubject.value;
}
GetUsername(): string {
if (localStorage.getItem('currentUser') != null)
return JSON.parse(localStorage.getItem('currentUser')).UserName;
return null;
}
login(username: string, password: string) {
return this.http.post<any>(`${Statics.ApiUrl}users/authenticate`, { Username: username, Password: password }, { responseType: "json" })
.pipe(map(user => {
debugger;
localStorage.setItem('user', JSON.stringify(user));
this.userSubject.next(user);
return user;
}));
}
logout() {
//remove user from local storage to log user out
localStorage.removeItem('user');
this.userSubject.next(null);
this.router.navigate(['/login']);
}
signUp(RegisterModel: RegisterModel): Observable<User> {
return this.http.post<User>(`${Statics.ApiUrl}users/signup`, RegisterModel, { responseType: "json" });
}
verifyUserPhone(username: string, password: string, verificationCode: string): Observable<User> {
return this.http.put<User>(Statics.ApiUrl + 'users/verifyUserPhone', { username, password, verificationCode }, { responseType: "json" });
}
}
这是我的授权人:
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private authenticationService: AuthenticationService
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
debugger;
const user = this.authenticationService.userValue;
var token_expiration=new Date(user?.TokenExpirartion);
var now=new Date();
if (user && token_expiration>now) {
// check if route is restricted by role
if (route.data.roles && route.data.roles.indexOf(user.role) === -1) {
// role not authorised so redirect to home page
this.router.navigate(['/']);
return false;
}
// authorized so return true
return true;
}
// not logged in so redirect to login page with the return url
this.authenticationService.logout();
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
return false;
}
}
我登录并获取用户数据并将其存储在本地存储中,但我的 authguard 从不激活路由,因为身份验证服务总是 return 用户 NULL.If 然后我只刷新页面有用。至于注销,它不会注销用户,直到我刷新页面。
见下文:
这是我的登录按钮 ts 代码:
this.AuthenticationService.login(this.username, this.password)
.pipe(first())
.subscribe({
next: () => {
// get return url from query parameters or default to home page
const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
this.router.navigateByUrl(returnUrl);
},
error: error => {
this.error = error;
this.submited = false;
}
});
当 authguard 在登录后检查 userSubject 时 returns null。无论我登录多少次,除非我刷新页面,否则它将 return 为空。 只有这样 userSubject 才会被评估。我不知道该怎么办。我无法登录我的网站。我必须登录然后刷新页面才能使 authguard 工作。
刷新页面后我得到值:
似乎授权服务需要一些时间来获取数据,到那时路由已被拒绝,因为它为空。
您可以尝试在 approving/rejecting 路线
之前添加一些等待你们的服务是在 root 中提供的吗?如果没有,您可能会得到多个实例。
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
...
这里的问题是您的 authguard 是同步的,并且在实际加载用户之前调用了 canActivate。如果您的 canActivate 将 return observable of boolean,而不是 boolean,您可以解决此问题。 首先,您需要添加函数,如果已经加载,用户将 return 可观察到该函数,并加载用户观察,如下所示:
public getOrFetchUser(): Observable<User> {
// please note that we return observable in both cases
return this.userSubject.value ? of(this.userSubject.value) : this.login();
}
然后将您的 canActivate 守卫更改为:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.authenticationService.getOrFetchUser().pipe(
map((user) => {
console.log('user, ', user);
// change return to your validation logic
return true;
});