Angular:解析器获取数据后的路由守卫及异常处理方案咨询
Angular发票详情页路由权限处理最佳方案
针对你提出的/invoices/:invoiceId路由的双重保护需求,结合守卫与解析器的执行顺序限制,以下是两种可行的解决方案及优劣分析:
方案一:在解析器中直接处理错误重定向(推荐)
解析器不仅可以返回数据,还支持返回UrlTree触发路由重定向,无需组件介入即可在路由层面完成拦截,完全符合权限校验的职责划分。
修改后的invoice.resolver.ts:
import { inject } from '@angular/core'; import { ResolveFn, Router } from '@angular/router'; import { InvoiceApiService } from './invoice-api.service'; import { catchError } from 'rxjs/operators'; import { of } from 'rxjs'; import { ToastService } from './toast.service'; // 用于错误提示的服务 export const invoiceResolver: ResolveFn<any> = (route) => { const invoiceId = parseInt(route.paramMap.get('invoiceId') ?? '0', 10); const invoiceApiService = inject(InvoiceApiService); const router = inject(Router); const toastService = inject(ToastService); return invoiceApiService.invoice(invoiceId).pipe( catchError((error) => { // 根据错误类型针对性处理 if ([403, 404].includes(error.status)) { toastService.showError('无权访问该发票'); } else { toastService.showError('服务器错误,请稍后重试'); } // 返回UrlTree触发首页重定向 return of(router.createUrlTree(['/'])); }) ); };
优势:
- 逻辑集中在路由层,组件无需处理权限校验逻辑
- 组件不会被加载,避免页面闪烁等不良体验
- 同时完成数据预加载与权限验证,符合Resolver的设计初衷
方案二:用CanActivate守卫加载并验证发票
若不想依赖Resolver,可新增一个CanActivate守卫,在守卫中完成发票加载、权限校验,并通过共享服务将数据传递给组件。
路由配置修改
{ path: 'invoice/:invoiceId', component: InvoiceComponent, canActivate: [authGuard, invoiceOwnerGuard] }
实现invoiceOwnerGuard.ts
import { inject } from '@angular/core'; import { CanActivateFn, Router } from '@angular/router'; import { InvoiceApiService } from './invoice-api.service'; import { catchError, map } from 'rxjs/operators'; import { of } from 'rxjs'; import { ToastService } from './toast.service'; import { InvoiceDataService } from './invoice-data.service'; // 共享数据服务 import { AuthenticationPolicy } from './authentication-policy'; export const invoiceOwnerGuard: CanActivateFn = (route) => { const invoiceId = parseInt(route.paramMap.get('invoiceId') ?? '0', 10); const invoiceApiService = inject(InvoiceApiService); const router = inject(Router); const toastService = inject(ToastService); const invoiceDataService = inject(InvoiceDataService); const authPolicy = inject(AuthenticationPolicy); return invoiceApiService.invoice(invoiceId).pipe( map(invoice => { // 校验发票归属当前登录用户 const currentUserId = authPolicy.getCurrentUserId(); if (invoice.userId === currentUserId) { invoiceDataService.setInvoice(invoice); // 存储数据供组件使用 return true; } else { toastService.showError('无权访问该发票'); return router.createUrlTree(['/']); } }), catchError(() => { toastService.showError('加载发票失败'); return of(router.createUrlTree(['/'])); }) ); };
组件中获取数据
import { Component, OnInit } from '@angular/core'; import { InvoiceDataService } from './invoice-data.service'; @Component({ selector: 'app-invoice', templateUrl: './invoice.component.html' }) export class InvoiceComponent implements OnInit { invoice: Invoice | null = null; constructor(private invoiceDataService: InvoiceDataService) {} ngOnInit() { this.invoice = this.invoiceDataService.getInvoice(); } }
优势:将所有权限校验逻辑集中在守卫层,职责清晰;劣势:需要额外维护共享数据服务,不如Resolver预加载数据的方式简洁。
不推荐方案:组件中处理解析为null的情况
若Resolver返回null后在组件内判断重定向,会导致组件短暂初始化(可能出现页面闪烁),违反路由层负责权限拦截的单一职责原则,同时分散了逻辑,不利于维护。
内容的提问来源于stack exchange,提问作者schaenk
相关产品推荐
相关产品推荐

