Angular 22.2 守卫抛出 RedirectCommand,后面的跳转抢在前面生效
Angular 22.2 的后置守卫抛出 RedirectCommand,可打断前置异步结果的优先级。改用正常返回通道,并核对并发守卫的业务依赖和导航结果。
路由按 canActivate: [isLoggedIn, hasPlan] 配置:前一个守卫异步决定去登录页,后一个守卫却先把用户带到了升级页。若后者使用 throw new RedirectCommand(...),要区分“返回跳转结果”和“抛出跳转命令”两条路径。
Angular 22.2 支持抛出 RedirectCommand,但 22.2.2 的这组并发守卫可以绕过数组优先级。22.2.3 的正式守卫组合代码仍保留同样的边界;后续主分支上的处理不能直接算作这些正式版本已经修复。
守卫数组不是逐个 await
路由器按数组顺序启动守卫,却不会等第一个异步守卫完成才运行下一个。正常结果进入优先级组合:前面尚未完成时,后面的结果先等待;第一个不为 true 的结果最终决定导航。
抛出的 RedirectCommand 会走错误通道。受影响实现的组合在前面的异步结果尚未到达时就被这个错误打断,外层随即处理了后一个守卫的跳转。简单交换数组顺序可能改变某次结果,却没有修正这条错误通道。
对于原本只需决定允许、拒绝或跳转的守卫,优先返回结果。下面的函数可放在已有守卫文件,路由组件和其他权限判断沿用项目原配置:
import { inject } from '@angular/core';
import { CanActivateFn, RedirectCommand, Router } from '@angular/router';
export const hasPlan: CanActivateFn = () => {
const router = inject(Router);
return new RedirectCommand(router.parseUrl('/upgrade'));
};
这段只演示需要跳转的分支;真实的已订阅用户仍应返回 true。若改成异步函数,使用 return 返回命令,不在 Promise 或 Observable 内把它转成拒绝或 error。改变外层函数的 async 关键字,本身不会把异常变成普通 GuardResult。
先修结果通道,再核对业务依赖
检查同一路由数组里其他守卫是否也通过 throw、Promise.reject 或流错误表达跳转。只把明确的 RedirectCommand 改为正常结果,普通编程错误与服务故障仍按应用原有错误策略处理,不要把所有异常都当成允许导航。
如果 hasPlan 要用 isLoggedIn 刚加载的用户信息,数组顺序也不保证这种依赖成立。可以让两个守卫共享同一条已定义的加载流程,或合成一个明确串联的守卫,再决定登录与套餐的业务优先级。不要一边依赖串行执行,一边仅用数组先后表达它。
客户端守卫也不能代替服务端鉴权。这里修的是前端导航去向,返回正确登录页并不证明受保护接口的权限检查已经完整。
保留前一个守卫延迟完成的条件验收:前者返回登录跳转时,后者即使更早返回升级跳转,也应等待前者并选择登录页;前者返回 true 后,才轮到后者的跳转;两者都返回 true 时正常进入页面。只测试同步守卫,容易漏掉这个差异。改动前后保留原路由配置和异常记录,若采用未来正式修复,仍用这组排列复测,不仅以构建成功作判断。


