Angular 4, Router 4.0.0: routerLink or router.navigate programmatically doesn't redirect to child routes
Angular 4, Router 4.0.0: routerLink or router.navigate programatically doesn't redirect to child routes
我正在尝试创建一个 link 以在登录(/signin 路由)后重定向到 /dashboard/overview 子路由,但没有任何运气。我单击 link 并且没有收到任何错误或任何响应。我可以看到浏览器底部栏上的路径是正确的,如果我手动输入 url 我会访问正确的页面,路径是 /dashboard/overview .我不确定它是否有任何关系的一件事是该路由是 AuthGuarded。
我在登录后以编程方式尝试了这两种方法,将用户重定向到仪表板,我什至可以在 chrome 控制台
上看到 'redirecting to dashboard' 消息
onSignin(form: NgForm){
const email = form.value.email;
const password = form.value.password;
this.user = {email, password};
this.authServiceSubscription = this.authService.signinUser(this.user).subscribe(
(response) => {
//redirect to dashboard
const loginResultCode = response.login_result_code;
if (loginResultCode == "SUCCESS") {
console.log("Sponsor logged in");
this.authService.changeStatusToAuthenticated();
//redirect to dashboard
console.log('Redirecting to dashboard');
this.router.navigate(['/dashboard/overview']);
} else {
console.log("There were errors with the data");
//present errors to the user
this.errorMessage = "Los datos de autenticación son incorrectos. Intente nuevamente";
}
},
(error) => { console.log("Error Login", error); this.errorMessage = "Hubo un error interno, intente de nuevo mas tarde";}
);
}
并且还创建了一个 routerLink,但它也不起作用,没有任何反应,甚至控制台中也没有错误:
<li><a style="cursor: pointer;" routerLink="/dashboard/overview">Go To Dashboard</a></li>
这是我的路由文件:
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}
] },
{ path: 'not-found', component: ErrorPageComponent },
{ path: '**', redirectTo: '/not-found' }
]
我什至在 Dashboard 组件的 ngOnInit 上放了一个 console.log 来查看组件是否被创建,或者在概览组件中放了一个 console.log 但我没有任何运气,我看不到任何消息以编程方式或使用 routerLink 导航时在控制台上。如上所述,我在手动访问时确实收到了消息。有任何想法吗?非常感谢
编辑:显然是我应用到仪表板路由的 authguard 有问题,这是 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 => {
if (isAuth){
console.log("Auth Guard approves the access");
return true;
}
else {
console.log('AuthGuard Denying route, redirecting to signin');
this.router.navigate(['/signin']);
return false;
}
});
}
}
authService 上的 isAuthenticated() 方法只是 returns 具有用户身份验证状态的可观察对象。我想知道是否存在竞争条件或其他问题……因为最初通过发出 http 异步请求设置可观察对象……如果我在 isAuthenticated 方法中输入 console.log,它会记录在控制台上。如果我将 console.log 放在地图中的 authguard 函数中,如果它没有被记录,那么由于某种原因代码没有被执行....
auth.service.ts
import { Injectable, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Http, Response, RequestOptions, Headers } from '@angular/http';
import 'rxjs/add/operator/map';
import {Observable, Subject} from "rxjs/Rx";
@Injectable()
export class AuthService implements OnInit {
userIsAuthenticated = new Subject();
constructor(private router: Router, private http: Http) {
this.ngOnInit();
}
private getHeaders(){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Authorization','Bearer');
return headers;
}
ngOnInit(){
this.changeStatusToUnauthenticated();
//initial check with the server
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
this.http.get('http://localhost:3000/api/sponsor/check/login',options)
.map(response => {
console.log("Execute this");
if (response.status === 200) {
console.log("execute also this");
this.changeStatusToAuthenticated();
return Observable.of(true);
}
}
).catch((err)=>{
//maybe add in the future if the code is 403 then send him to login otherwise send him elsewhere
if(err.status === 403){
console.log('Forbidden 403');
// If I want to redirect the user uncomment this line
// this.router.navigate(['/signin']);
}
this.changeStatusToUnauthenticated();
return Observable.of(false);
}).subscribe((isAuth)=>{
console.log("Initial refresh auth state ", isAuth);
});
}
isAuthenticated(): Observable<boolean> {
if(this.userIsAuthenticated){
//if I change this line for return Observable.of(true) it works
return this.userIsAuthenticated;
}else{
return Observable.of(false);
}
}
logout() {
console.log('logging out');
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.get('http://localhost:3000/api/sponsor/logout/', options).map(res=>res.json())
.subscribe(
(response) => {
//redirect to dashboard
const logoutResultCode = response.code;
if (logoutResultCode == "200") {
console.log("Sponsor logged out successfully");
//redirect to dashboard
this.changeStatusToUnauthenticated();
this.router.navigate(['/signin']);
}
},
(error) => {
console.log("Error Logout- Header", error);
//check for 403 if it's forbidden or a connection error
this.changeStatusToUnauthenticated();
this.router.navigate(['/signin']);}
);
}
signinUser(user) {
console.log("Logging user");
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.post('http://localhost:3000/api/sponsor/login/', user, options).map(
response => response.json());
}
registerUser(user) {
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.post('http://localhost:3000/api/sponsor/register/', user, options).map(
response => response.json());
}
changeStatusToUnauthenticated(){
this.userIsAuthenticated.next(false);
}
changeStatusToAuthenticated(){
this.userIsAuthenticated.next(true);
}
}
编辑 2:我在 authService 上使用了 Behavior Subject 而不是 Subject,因为它可以让我获得最后发出的值,与您必须订阅的常规主题相比,这是一个非常酷的功能,有时这还不够.下面是我的答案的更多详细信息。
最终问题出在 isAuthenticated() 方法上的 authService returned,我显然没有根据日志 returning 解析值,所以 authguard 得到了在能够解析到组件的路由之前卡住了。我通过搜索 rxjs 文档解决了我的问题。我找到了 BehaviorSubject https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md
它可以让你得到最后一个值,这样我就可以 return 一个 Observable.of(userIsAuthenticated.getValue()) 并将它传递给 AuthGuard,它现在可以完美地工作了。我添加了这样的逻辑,如果最后发出的值是假的,那么我会做一个虚拟请求来决定是否应该将用户发送到登录屏幕。然后,如果我对服务器发出的每个请求都收到 http 禁止响应,则将 BehaviourSubject 的值更改为 false。这些东西结合起来将确保前端和后端传统会话之间的一致性,避免后端的过期会话和前端的非过期状态。希望这对某人有帮助。代码:
auth.service.ts
@Injectable()
export class AuthService implements OnInit {
userIsAuthenticated= new BehaviorSubject(null);
constructor(private router: Router, private http: Http) {
this.ngOnInit();
}
private getHeaders(){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Authorization','Bearer');
return headers;
}
ngOnInit() {
//initial check with the server
this.doAuthCheck();
}
doAuthCheck(): Observable<boolean> {
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.get('http://localhost:3000/api/check/login', options)
.map(response => {
if (response.status === 200) {
this.changeStatusToAuthenticated();
return Observable.of(true);
}
}
).catch((err) => {
//maybe add in the future if the code is 403 then send him to login otherwise send him elsewhere
if (err.status === 403) {
console.log('Forbidden 403');
// If I want to redirect the user uncomment this line
// this.router.navigate(['/signin']);
}
this.changeStatusToUnauthenticated();
return Observable.of(false);
});
}
isAuthenticated(): Observable<boolean> {
const isAuth = this.userIsAuthenticated.getValue();
if (isAuth) {
return Observable.of(isAuth);
} else {
return this.doAuthCheck();
}
}
logout() {
console.log('logging out');
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.get('http://localhost:3000/api/logout/', options).map(res => res.json())
.subscribe(
(response) => {
//redirect to dashboard
const logoutResultCode = response.code;
if (logoutResultCode == "200") {
console.log("logged out successfully");
//redirect to dashboard
this.changeStatusToUnauthenticated();
this.router.navigate(['/signin']);
}
},
(error) => {
console.log("Error Logout- Header", error);
//check for 403 if it's forbidden or a connection error
this.changeStatusToUnauthenticated();
this.router.navigate(['/signin']);
}
);
}
signinUser(user) {
console.log("Logging user");
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.post('http://localhost:3000/api/login/', user, options).map(
response => response.json());
}
registerUser(user) {
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.post('http://localhost:3000/api/register/', user, options).map(
response => response.json());
}
changeStatusToUnauthenticated() {
this.userIsAuthenticated.next(false);
}
changeStatusToAuthenticated() {
this.userIsAuthenticated.next(true);
}
}
auth-guard.service.ts
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){
console.log("Auth Guard approves the access");
return true;
}
else {
console.log('AuthGuard Denying route, redirecting to signin');
this.router.navigate(['/signin']);
return false;
}
});
}
}
路由文件
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}
] },
{ path: 'not-found', component: ErrorPageComponent },
{ path: '**', redirectTo: '/not-found' }
]
我正在尝试创建一个 link 以在登录(/signin 路由)后重定向到 /dashboard/overview 子路由,但没有任何运气。我单击 link 并且没有收到任何错误或任何响应。我可以看到浏览器底部栏上的路径是正确的,如果我手动输入 url 我会访问正确的页面,路径是 /dashboard/overview .我不确定它是否有任何关系的一件事是该路由是 AuthGuarded。
我在登录后以编程方式尝试了这两种方法,将用户重定向到仪表板,我什至可以在 chrome 控制台
上看到 'redirecting to dashboard' 消息onSignin(form: NgForm){
const email = form.value.email;
const password = form.value.password;
this.user = {email, password};
this.authServiceSubscription = this.authService.signinUser(this.user).subscribe(
(response) => {
//redirect to dashboard
const loginResultCode = response.login_result_code;
if (loginResultCode == "SUCCESS") {
console.log("Sponsor logged in");
this.authService.changeStatusToAuthenticated();
//redirect to dashboard
console.log('Redirecting to dashboard');
this.router.navigate(['/dashboard/overview']);
} else {
console.log("There were errors with the data");
//present errors to the user
this.errorMessage = "Los datos de autenticación son incorrectos. Intente nuevamente";
}
},
(error) => { console.log("Error Login", error); this.errorMessage = "Hubo un error interno, intente de nuevo mas tarde";}
);
}
并且还创建了一个 routerLink,但它也不起作用,没有任何反应,甚至控制台中也没有错误:
<li><a style="cursor: pointer;" routerLink="/dashboard/overview">Go To Dashboard</a></li>
这是我的路由文件:
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}
] },
{ path: 'not-found', component: ErrorPageComponent },
{ path: '**', redirectTo: '/not-found' }
]
我什至在 Dashboard 组件的 ngOnInit 上放了一个 console.log 来查看组件是否被创建,或者在概览组件中放了一个 console.log 但我没有任何运气,我看不到任何消息以编程方式或使用 routerLink 导航时在控制台上。如上所述,我在手动访问时确实收到了消息。有任何想法吗?非常感谢
编辑:显然是我应用到仪表板路由的 authguard 有问题,这是 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 => {
if (isAuth){
console.log("Auth Guard approves the access");
return true;
}
else {
console.log('AuthGuard Denying route, redirecting to signin');
this.router.navigate(['/signin']);
return false;
}
});
}
}
authService 上的 isAuthenticated() 方法只是 returns 具有用户身份验证状态的可观察对象。我想知道是否存在竞争条件或其他问题……因为最初通过发出 http 异步请求设置可观察对象……如果我在 isAuthenticated 方法中输入 console.log,它会记录在控制台上。如果我将 console.log 放在地图中的 authguard 函数中,如果它没有被记录,那么由于某种原因代码没有被执行....
auth.service.ts
import { Injectable, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Http, Response, RequestOptions, Headers } from '@angular/http';
import 'rxjs/add/operator/map';
import {Observable, Subject} from "rxjs/Rx";
@Injectable()
export class AuthService implements OnInit {
userIsAuthenticated = new Subject();
constructor(private router: Router, private http: Http) {
this.ngOnInit();
}
private getHeaders(){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Authorization','Bearer');
return headers;
}
ngOnInit(){
this.changeStatusToUnauthenticated();
//initial check with the server
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
this.http.get('http://localhost:3000/api/sponsor/check/login',options)
.map(response => {
console.log("Execute this");
if (response.status === 200) {
console.log("execute also this");
this.changeStatusToAuthenticated();
return Observable.of(true);
}
}
).catch((err)=>{
//maybe add in the future if the code is 403 then send him to login otherwise send him elsewhere
if(err.status === 403){
console.log('Forbidden 403');
// If I want to redirect the user uncomment this line
// this.router.navigate(['/signin']);
}
this.changeStatusToUnauthenticated();
return Observable.of(false);
}).subscribe((isAuth)=>{
console.log("Initial refresh auth state ", isAuth);
});
}
isAuthenticated(): Observable<boolean> {
if(this.userIsAuthenticated){
//if I change this line for return Observable.of(true) it works
return this.userIsAuthenticated;
}else{
return Observable.of(false);
}
}
logout() {
console.log('logging out');
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.get('http://localhost:3000/api/sponsor/logout/', options).map(res=>res.json())
.subscribe(
(response) => {
//redirect to dashboard
const logoutResultCode = response.code;
if (logoutResultCode == "200") {
console.log("Sponsor logged out successfully");
//redirect to dashboard
this.changeStatusToUnauthenticated();
this.router.navigate(['/signin']);
}
},
(error) => {
console.log("Error Logout- Header", error);
//check for 403 if it's forbidden or a connection error
this.changeStatusToUnauthenticated();
this.router.navigate(['/signin']);}
);
}
signinUser(user) {
console.log("Logging user");
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.post('http://localhost:3000/api/sponsor/login/', user, options).map(
response => response.json());
}
registerUser(user) {
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.post('http://localhost:3000/api/sponsor/register/', user, options).map(
response => response.json());
}
changeStatusToUnauthenticated(){
this.userIsAuthenticated.next(false);
}
changeStatusToAuthenticated(){
this.userIsAuthenticated.next(true);
}
}
编辑 2:我在 authService 上使用了 Behavior Subject 而不是 Subject,因为它可以让我获得最后发出的值,与您必须订阅的常规主题相比,这是一个非常酷的功能,有时这还不够.下面是我的答案的更多详细信息。
最终问题出在 isAuthenticated() 方法上的 authService returned,我显然没有根据日志 returning 解析值,所以 authguard 得到了在能够解析到组件的路由之前卡住了。我通过搜索 rxjs 文档解决了我的问题。我找到了 BehaviorSubject https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md
它可以让你得到最后一个值,这样我就可以 return 一个 Observable.of(userIsAuthenticated.getValue()) 并将它传递给 AuthGuard,它现在可以完美地工作了。我添加了这样的逻辑,如果最后发出的值是假的,那么我会做一个虚拟请求来决定是否应该将用户发送到登录屏幕。然后,如果我对服务器发出的每个请求都收到 http 禁止响应,则将 BehaviourSubject 的值更改为 false。这些东西结合起来将确保前端和后端传统会话之间的一致性,避免后端的过期会话和前端的非过期状态。希望这对某人有帮助。代码:
auth.service.ts
@Injectable()
export class AuthService implements OnInit {
userIsAuthenticated= new BehaviorSubject(null);
constructor(private router: Router, private http: Http) {
this.ngOnInit();
}
private getHeaders(){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Authorization','Bearer');
return headers;
}
ngOnInit() {
//initial check with the server
this.doAuthCheck();
}
doAuthCheck(): Observable<boolean> {
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.get('http://localhost:3000/api/check/login', options)
.map(response => {
if (response.status === 200) {
this.changeStatusToAuthenticated();
return Observable.of(true);
}
}
).catch((err) => {
//maybe add in the future if the code is 403 then send him to login otherwise send him elsewhere
if (err.status === 403) {
console.log('Forbidden 403');
// If I want to redirect the user uncomment this line
// this.router.navigate(['/signin']);
}
this.changeStatusToUnauthenticated();
return Observable.of(false);
});
}
isAuthenticated(): Observable<boolean> {
const isAuth = this.userIsAuthenticated.getValue();
if (isAuth) {
return Observable.of(isAuth);
} else {
return this.doAuthCheck();
}
}
logout() {
console.log('logging out');
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.get('http://localhost:3000/api/logout/', options).map(res => res.json())
.subscribe(
(response) => {
//redirect to dashboard
const logoutResultCode = response.code;
if (logoutResultCode == "200") {
console.log("logged out successfully");
//redirect to dashboard
this.changeStatusToUnauthenticated();
this.router.navigate(['/signin']);
}
},
(error) => {
console.log("Error Logout- Header", error);
//check for 403 if it's forbidden or a connection error
this.changeStatusToUnauthenticated();
this.router.navigate(['/signin']);
}
);
}
signinUser(user) {
console.log("Logging user");
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.post('http://localhost:3000/api/login/', user, options).map(
response => response.json());
}
registerUser(user) {
let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
return this.http.post('http://localhost:3000/api/register/', user, options).map(
response => response.json());
}
changeStatusToUnauthenticated() {
this.userIsAuthenticated.next(false);
}
changeStatusToAuthenticated() {
this.userIsAuthenticated.next(true);
}
}
auth-guard.service.ts
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){
console.log("Auth Guard approves the access");
return true;
}
else {
console.log('AuthGuard Denying route, redirecting to signin');
this.router.navigate(['/signin']);
return false;
}
});
}
}
路由文件
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}
] },
{ path: 'not-found', component: ErrorPageComponent },
{ path: '**', redirectTo: '/not-found' }
]