我没有找到标准的解决方案,并且Internet上提供的解决方案仅限于一页。因此,我写了我自己的并决定分享。
案例说明
应用程序页面分为3组:
- 仅适用于授权用户
- 仅针对未经授权的用户
- 对于任何用户
您可以在任何页面上登录或注销。
如果您在访问权限受限的页面上输入/退出,则需要进入允许的页面。
如果页面没有限制,那么您需要停留在当前页面上。
为了更好地理解,建议您了解以下信息:
决断
页面访问权限的区分是通过后卫-CanActivate进行的。当您转到页面时,将执行检查,但是在完成转换后,不会对权限更改做出反应。为避免访问权限逻辑重复,我们强行重启防护装置。
通过导航当前URL来重新启动当前页面的防护。并更改了Router.onSameUrlNavigation和Route.runGuardsAndResolvers策略。
这是一个现成的解决方案。下一节将提供更多详细信息。
import { Injectable } from '@angular/core';
import { ActivatedRoute, PRIMARY_OUTLET, Router, RunGuardsAndResolvers } from '@angular/router';
@Injectable()
export class GuardControlService {
constructor(
private route: ActivatedRoute,
private router: Router,
) {}
/**
* guard- url
*/
forceRunCurrentGuards(): void {
// Router.onSameUrlNavigation url
const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');
// ActivatedRoute primary outlet
const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);
// runGuardsAndResolvers ActivatedRoute url
const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');
//
this.router.navigateByUrl(
this.router.url
).then(() => {
// onSameUrlNavigation
restoreSameUrl();
// runGuardsAndResolvers
restoreRunGuards();
});
}
/**
* onSameUrlNavigation
* @param router - Router,
* @param strategy -
* @return callback
*/
private changeSameUrlStrategy(router: Router, strategy: 'reload' | 'ignore'): () => void {
const onSameUrlNavigation = router.onSameUrlNavigation;
router.onSameUrlNavigation = strategy;
return () => {
router.onSameUrlNavigation = onSameUrlNavigation;
}
}
/**
* route outlet-
* @param route - Route
* @param outlet - outlet-,
* @return ActivatedRoute outlet
*/
private getLastRouteForOutlet(route: ActivatedRoute, outlet: string): ActivatedRoute {
if (route.children?.length) {
return this.getLastRouteForOutlet(
route.children.find(item => item.outlet === outlet),
outlet
);
} else {
return route;
}
}
/**
* runGuardsAndResolvers ActivatedRoute ,
* @param route - ActivatedRoute
* @param strategy -
* @return callback
*/
private changeRunGuardStrategies(route: ActivatedRoute, strategy: RunGuardsAndResolvers): () => void {
const routeConfigs = route.pathFromRoot
.map(item => {
if (item.routeConfig) {
const runGuardsAndResolvers = item.routeConfig.runGuardsAndResolvers;
item.routeConfig.runGuardsAndResolvers = strategy;
return runGuardsAndResolvers;
} else {
return null;
}
});
return () => {
route.pathFromRoot
.forEach((item, index) => {
if (item.routeConfig) {
item.routeConfig.runGuardsAndResolvers = routeConfigs[index];
}
});
}
}
}
附加解决方案说明
您想要尝试重新启动防护措施的第一件事是使用导航到当前URL。
this.router.navigateByUrl(this.router.url);
但是,默认情况下,当前的URL转换事件将被忽略,并且不会发生任何事情。为此,您需要配置路由。
设置路由
1.更改路由器策略。onSameUrlNavigation
onSameUrlNavigation可以采用以下值:
onSameUrlNavigation: 'reload' | 'ignore';
为了敏感地转换到当前网址,您需要设置“重新加载”。
策略更改不会重新加载,但会生成其他导航事件。可以通过订阅获得:
this.router.events.subscribe();
2.更改路线策略。runGuardsAndResolvers
runGuardsAndResolvers可以采用以下值:
type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' | 'paramsChange' | 'paramsOrQueryParamsChange' | 'always' | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);
为了敏感地转换到当前网址,您需要设置“始终”。
在应用程序配置期间配置路由
onSameUrlNavigation:
const routes: : Route[] = [];
@NgModule({
imports: [
RouterModule.forRoot(
routes,
{ onSameUrlNavigation: 'reload' }
)
]
})
runGuardsAndResolvers:
const routes: Route[] = [
{
path: '',
component: AppComponent,
runGuardsAndResolvers: 'always',
}
];
在运行时配置路由
constructor(
private router: Router,
private route: ActivatedRoute
) {
this.router.onSameUrlNavigation = 'reload';
this.route.routeConfig.runGuardsAndResolvers = 'always';
}
重新启动警卫
要重新启动一个特定页面的防护,只需在配置过程中配置路由即可。
但是要重新加载任何页面的防护,在每个Route中更改runGuardsAndResolvers将导致不必要的检查。并且需要始终记住该参数-出错。
由于我们的案例假设任何页面都可以重新启动,而没有设置应用程序的限制,因此您需要:
1.替换onSameUrlNavigation并保留当前值
// Router.onSameUrlNavigation url
const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');
...
/**
* onSameUrlNavigation
* @param router - Router,
* @param strategy -
* @return callback
*/
private changeSameUrlStrategy(router: Router, strategy: 'reload' | 'ignore'): () => void {
const onSameUrlNavigation = router.onSameUrlNavigation;
router.onSameUrlNavigation = strategy;
return () => {
router.onSameUrlNavigation = onSameUrlNavigation;
}
}
2.获取当前网址的ActivatedRoute
由于注入的ActivatedRoute是在服务中执行的,因此生成的ActivatedRoute不与当前URL关联。
当前网址的ActivatedRoute位于最后一个主要出口,您需要找到它:
// ActivatedRoute primary outlet
const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);
...
/**
* route outlet-
* @param route - Route
* @param outlet - outlet-,
* @return ActivatedRoute outlet
*/
private getLastRouteForOutlet(route: ActivatedRoute, outlet: string): ActivatedRoute {
if (route.children?.length) {
return this.getLastRouteForOutlet(
route.children.find(item => item.outlet === outlet),
outlet
);
} else {
return route;
}
}
3.为所有ActivatedRoute及其祖先替换runGuardsAndResolvers,并保留当前值
限制访问的保护可以位于当前ActivatedRoute的任何祖先中。所有祖先都位于pathFromRoot中。
// runGuardsAndResolvers ActivatedRoute url
const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');
...
/**
* runGuardsAndResolvers ActivatedRoute ,
* @param route - ActivatedRoute
* @param strategy -
* @return callback
*/
private changeRunGuardStrategies(route: ActivatedRoute, strategy: RunGuardsAndResolvers): () => void {
const routeConfigs = route.pathFromRoot
.map(item => {
if (item.routeConfig) {
const runGuardsAndResolvers = item.routeConfig.runGuardsAndResolvers;
item.routeConfig.runGuardsAndResolvers = strategy;
return runGuardsAndResolvers;
} else {
return null;
}
});
return () => {
route.pathFromRoot
.forEach((item, index) => {
if (item.routeConfig) {
item.routeConfig.runGuardsAndResolvers = routeConfigs[index];
}
});
}
}
4.转到当前网址
this.router.navigateByUrl(this.router.url);
5.将runGuardsAndResolvers和onSameUrlNavigation返回其原始状态
restoreRunGuards();
restoreSameUrl();
6.将阶段合并为一个功能
constructor(
private route: ActivatedRoute,
private router: Router,
) {}
/**
* guard- url
*/
forceRunCurrentGuards(): void {
// Router.onSameUrlNavigation url
const restoreSameUrl = this.changeSameUrlStrategy(this.router, 'reload');
// ActivatedRoute primary outlet
const primaryRoute: ActivatedRoute = this.getLastRouteForOutlet(this.route.root, PRIMARY_OUTLET);
// runGuardsAndResolvers ActivatedRoute url
const restoreRunGuards = this.changeRunGuardStrategies(primaryRoute, 'always');
//
this.router.navigateByUrl(
this.router.url
).then(() => {
// onSameUrlNavigation
restoreSameUrl();
// runGuardsAndResolvers
restoreRunGuards();
});
}
希望本文对您有所帮助。如果还有其他解决方案,我将很高兴在评论中看到它们。