角度9.重新启动当前的页面保护。触发当前的路线警卫

无论打开哪个页面,都需要重新启动当前页面的防护。



我没有找到标准的解决方案,并且Internet上提供的解决方案仅限于一页。因此,我写了我自己的并决定分享。



案例说明



应用程序页面分为3组:



  • 仅适用于授权用户

  • 仅针对未经授权的用户

  • 对于任何用户



您可以在任何页面上登录或注销。



如果您在访问权限受限的页面上输入/退出,则需要进入允许的页面。



如果页面没有限制,那么您需要停留在当前页面上。



为了更好地理解,建议您了解以下信息:



  • 守卫-CanActivate

  • 路线

  • 路由器

  • 激活路线

  • 路由器插座



决断



页面访问权限的区分是通过后卫-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();
  });
}





希望本文对您有所帮助。如果还有其他解决方案,我将很高兴在评论中看到它们。



All Articles