路由守卫--防止未经授权的访问(angular11)
任何用户在任何时候都可以导航到任何地方,但有时候出于种种原因需要控制对应用不同部分的访问,这时可以使用路由守卫防止用户未经授权就导航到应用的某些部分。
angular提供了路由守卫
守卫会返回一个值,以控制路由器的行为:
- 返回true:导航继续
- 返回false:终止,且用户会留在原地
- 返回URLTree,取消当前导航,开始导航到URLTree
简单示例:
创建一个路由守卫在终端中执行
ng g g my-guard
angularcli会给我们自动生成一个路由守卫文件,打开该文件
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';@Injectable({providedIn: 'root'
})
export class MyGuardGuard implements CanActivate {canActivate(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {// ?判断逻辑写这里return true;}}
之后在路由文件中加入守卫,这样一个简单的守卫就写好了,只有通过了守卫的校验,用户才可以进行访问
{path: '/your-path',component: YourComponent,canActivate: [YourGuard],}
路由器可以支持多种守卫接口:
守卫可以用同步的方式返回一个boolean,但大多数时候可能会涉及接口访问,数据传输。。。异步操作,因此守卫还可以返回一个Observable或者Promise,路由器会等待可观察对象最后被解析为一个true或者false,如果这个可观察对象不能被结束,那么路由会一直留在原地
用CanActivate来处理导航到某路由的情况。
用CanActivateChild来处理导航到某子路由的情况。
用CanDeactivate来处理从当前路由离开的情况.
用Resolve在路由激活之前获取路由数据。
用CanLoad来处理异步导航到某特性模块的情况。
下面将会详细介绍一下这几种守卫接口
CanActivate
源码:
export declare interface CanActivate {canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
}
说明:一个接口,某些类可以实现它以扮演一个守卫,来决定该路由能否激活。如果所有守卫都返回了 true,那么导航将继续。如果任何守卫返回 false,则导航将被取消。如果任何守卫返回 UrlTree ,当前导航被取消,新的导航开始到守卫所返回的 UrlTree。
方法:
CanActivateChild
参数
route ActivatedRouteSnapshot ActivatedRouteSnapshot 包含了即将被激活的路由
state RouterStateSnapshot RouterStateSnapshot 包含了该应用即将到达的状态。
返回值
Observable
CanActivateChild
源码:
export declare interface CanActivateChild {canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
}
说明:
CanActivateChild 守卫和 CanActivate 守卫很像。 它们的区别在于,CanActivateChild 会在任何子路由被激活之前运行。实现 CanActivateChild 方法,它所接收的参数与 CanActivate 方法一样:一个 ActivatedRouteSnapshot 和一个 RouterStateSnapshot。 CanActivateChild 方法可以返回 Observable
方法:
参数
childRoute ActivatedRouteSnapshot
state RouterStateSnapshot
返回值
Observable
CanDeactivate
很多时候需要积累来自用户的更改,跨字段验证,在服务器上验证,或者把变更保持在待定状态,直到用户确认这一组字段或取消并还原所有变更为止。当用户要从当前路由离开时,需要让用户选择他所作的更改是否要保存或者取消,一般保存都是异步操作,如果让用户立即进入下一个页面,而保存失败了 ,就会丢失错误的上下文环境,这个时候就需要在服务器返回响应之前停止导航。
CanDeactivate 守卫能帮助你决定如何处理未保存的更改,以及如何处理。
简单来讲所有跳转之前要做的异步操作:比如说跳出一个确认页面? 保存数据? 和服务器通讯。。。都可以借助CanDeactivate来实现。
使用示例:
守卫代码:
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';export interface CanComponentDeactivate {canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}@Injectable({providedIn: 'root',
})
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {canDeactivate(component: CanComponentDeactivate) {return component.canDeactivate ? component.canDeactivate() : true;}
}
组件代码:
canDeactivate(): Observable<boolean> | boolean {// Allow synchronous navigation (`true`) if no crisis or the crisis is unchangedif (!this.change) {return true;}return this.service.confirm('Discard changes?');
}
路由:
children: [{path: ':id',component: CrisisDetailComponent,canDeactivate: [CanDeactivateGuard]},
源码:
interface CanDeactivate<T> {canDeactivate(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
}
方法:canDeactivate
参数
component T
currentRoute ActivatedRouteSnapshot
currentState RouterStateSnapshot
nextState RouterStateSnapshot 可选. 默认值是 undefined.
返回值
Observable
Resolve: 预先获取组件数据
一般情况下,我们都是先跳转路由,在从服务器获取数据,如果响应慢,就会先显示一个空的组件;或者当用户从列表选择一个选项进入详情之后,才知道该选项内容不存在,这些对用户体验都不是很好。最好使用解析器预先从服务器上获取完数据,这样在路由激活的那一刻数据就准备好了。
总之,如果你希望的是只有当所有必要数据都已经拿到之后,才渲染这个路由组件,那么你可以试试resolve。
注意:
- 路由器的这个 Resolve 接口是可选的。CrisisDetailResolverService
没有继承自某个基类。路由器只要找到了这个方法,就会调用它。 - 路由器会在用户可以导航的任何情况下调用该解析器,这样你就不用针对每个用例都编写代码了。
- 在任何一个解析器中返回空的 Observable 就会取消导航。
***使用说明:如果同时指定了守卫和解析器,则直到所有守卫都运行并成功后,解析器才***会执行。
使用示例:
路由:
children: [{path: ':id',component: CrisisDetailComponent,canDeactivate: [CanDeactivateGuard],resolve: {crisis: CrisisDetailResolverService}},
守卫:将组件中ngoninit中的代码移动到守卫里面
import { Injectable } from '@angular/core';
import {Router, Resolve,RouterStateSnapshot,ActivatedRouteSnapshot
} from '@angular/router';
import { Observable, of, EMPTY } from 'rxjs';
import { mergeMap, take } from 'rxjs/operators';@Injectable({providedIn: 'root',
})
export class CrisisDetailResolverService implements Resolve<Crisis> {constructor(private cs: CrisisService, private router: Router) {}resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Crisis> | Observable<never> {// ?ngoninit代码}
}
组件:在 ActivatedRoute.data中就可以直接得到数据
ngOnInit() {this.route.data.subscribe(data => {});
}
源码:
interface Resolve<T> {resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T
}
resolve()
参数
route ActivatedRouteSnapshot
state RouterStateSnapshot
返回值
Observable | Promise | T
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
