路由守衛(Route Guards)
使用場景
路由守衛可以幫助我們控制用戶對特定路由的訪問權限。例如:
希望使用者在登入前只能訪問登入、註冊頁面
希望使用者在登入後無法訪問登入、註冊頁面
防止用戶在表單未保存的情況下離開頁面
確保某些數據在載入視圖前已經準備好
建立路由守衛
使用 Angular CLI 建立路由守衛:
ng generate guard core/guards/auth --functional
選項解釋:
CanActivate
:決定是否允許用戶訪問特定路由CanActivateChild
:控制子路由的訪問權限CanDeactivate
:控制從當前路由離開的權限(例如防止用戶未保存表單就離開)CanMatch
:決定路由配置是否應被包含在路由匹配過程中
路由守衛參數說明
在 Angular 的路由守衛中,route
和 state
這兩個參數提供了關於當前導航的重要信息:
1. route (ActivatedRouteSnapshot)
包含當前要激活的路由的訊息快照
可以訪問路由參數 (
route.params
)可以獲取路由配置中的數據 (
route.data
)可以獲取 URL 中的片段 (
route.fragment
)可以訪問查詢參數 (
route.queryParams
)可以訪問路由配置 (
route.routeConfig
)可以訪問父路由和子路由
2. state (RouterStateSnapshot)
包含整個路由器狀態的快照
提供當前 URL (
state.url
)包含整個路由樹的快照
可以訪問所有激活的路由
可以獲取完整的路由信息,包括根路由到當前路由的所有信息
實作範例
檢查用戶是否已登入的守衛
在這個例子中,我們建立兩個守衛:
authGuard
:確保只有已登入的用戶可以訪問受保護的頁面noAuthGuard
:確保已登入的用戶無法訪問登入和註冊頁面
// src/app/core/guards/auth.guard.ts
import { CanActivateFn, Router } from '@angular/router';
import { inject } from '@angular/core';
export const authGuard: CanActivateFn = (route, state) => {
const router = inject(Router);
const token = localStorage.getItem('auth_token');
if (token) {
return true;
}
// 儲存用戶嘗試訪問的 URL,以便在登入後重定向
localStorage.setItem('redirectUrl', state.url);
router.navigate(['/login']);
return false;
};
export const noAuthGuard: CanActivateFn = (route, state) => {
const router = inject(Router);
const token = localStorage.getItem('auth_token');
if (!token) {
return true;
}
router.navigate(['/event-list']);
return false;
};
處理路由參數的守衛範例
export const detailGuard: CanActivateFn = (route, state) => {
const id = route.paramMap.get('id');
// 檢查 ID 是否為有效值
if (!id || isNaN(+id) || +id <= 0) {
// 如果 ID 無效,重定向到列表頁面
const router = inject(Router);
router.navigate(['/item-list']);
return false;
}
return true;
};
在路由配置中使用守衛
// app.routes.ts
export const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent, canActivate: [noAuthGuard] },
{ path: 'register', component: RegisterComponent, canActivate: [noAuthGuard] },
{ path: 'event-list', component: EventListComponent, canActivate: [authGuard] },
{ path: 'event-detail/:id', component: EventDetailComponent, canActivate: [authGuard, detailGuard] },
// 如果沒有匹配的路由,重定向到登入頁面
{ path: '**', redirectTo: '/login' }
];
處理異步認證狀態
如果你的認證狀態需要從 API 檢查(例如驗證 token 是否仍有效),你可以使用 Observable 來處理:
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
return authService.isAuthenticated().pipe(
map(isAuth => {
if (isAuth) {
return true;
}
localStorage.setItem('redirectUrl', state.url);
router.navigate(['/login']);
return false;
})
);
};
注意事項
inject 函數的使用:
inject
函數只能在注入上下文中使用,不能在模組的頂層直接調用。在路由守衛內部使用是安全的,因為每次函數執行時都會建立新的注入上下文。組織方式:將相關的守衛函數放在同一個檔案中是合理的做法,尤其是當它們有相似的功能時。這樣可以:
便於管理和理解相關功能
共享共同的邏輯或注入的服務
減少需要導入的檔案數量
路由守衛的順序:如果在一個路由上定義了多個守衛,它們會按照定義的順序依次執行。只有當所有守衛都返回
true
時,用戶才能訪問該路由。
Last updated