کنترل دسترسی route با guardها
Route guardها functionهایی هستند که کنترل میکنند آیا کاربر میتواند به یک route مشخص navigate کند یا از آن خارج شود. آنها مثل checkpointهایی هستند که مدیریت میکنند کاربر اجازه دسترسی به routeهای مشخص را دارد یا نه. نمونههای رایج استفاده از route guardها شامل authentication و access control است.
ساخت یک route guard
میتوانید با Angular CLI یک route guard generate کنید:
ng generate guard CUSTOM_NAMEاین دستور از شما میخواهد انتخاب کنید از کدام نوع route guard استفاده شود و سپس فایل متناظر CUSTOM_NAME-guard.ts را میسازد.
نوعهای return در route guard
همه route guardها نوعهای return ممکن یکسانی دارند. این موضوع در نحوه کنترل navigation به شما flexibility میدهد:
| Return types | Description |
|---|---|
boolean | true اجازه navigation میدهد، false آن را block میکند؛ note مربوط به CanMatch route guard را ببینید |
UrlTree یا RedirectCommand | بهجای block کردن، به route دیگری redirect میکند |
Promise<T> یا Observable<T> | Router از اولین value emitشده استفاده میکند و سپس unsubscribe میکند |
نوعهای route guard
Angular چهار نوع route guard فراهم میکند که هرکدام هدف متفاوتی دارند:
همه guardها به serviceهایی که در سطح route provide شدهاند و همچنین از طریق argument مربوط به route به اطلاعات مخصوص route دسترسی دارند.
CanActivate
Guard مربوط به CanActivate مشخص میکند آیا کاربر میتواند به یک route دسترسی پیدا کند یا نه. این guard بیشتر برای authentication و authorization استفاده میشود.
به argumentهای پیشفرض زیر دسترسی دارد:
route:ActivatedRouteSnapshot- شامل اطلاعاتی درباره routeای که در حال فعال شدن استstate:RouterStateSnapshot- شامل state فعلی router
میتواند نوعهای return استاندارد guard را برگرداند.
export const authGuard: CanActivateFn = (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
) => {
const authService = inject(AuthService);
return authService.isAuthenticated();
};Tip: اگر لازم دارید کاربر را redirect کنید، یک URLTree یا RedirectCommand برگردانید. مقدار false برنگردانید و بعد کاربر را بهصورت programmatic navigate نکنید.
برای اطلاعات بیشتر، API docs مربوط به CanActivateFn را ببینید.
CanActivateChild
Guard مربوط به CanActivateChild مشخص میکند آیا کاربر میتواند به child routeهای یک route والد خاص دسترسی پیدا کند یا نه. این guard وقتی مفید است که میخواهید یک section کامل از nested routeها را محافظت کنید. به بیان دیگر، canActivateChild برای همه childها اجرا میشود. اگر یک child component خودش component فرزند دیگری زیرمجموعه داشته باشد، canActivateChild برای هر دو component یک بار اجرا میشود.
به argumentهای پیشفرض زیر دسترسی دارد:
childRoute:ActivatedRouteSnapshot- شامل اطلاعاتی درباره snapshot «آینده»، یعنی stateای که router تلاش دارد به آن navigate کند، برای child routeای که در حال فعال شدن استstate:RouterStateSnapshot- شامل state فعلی router
میتواند نوعهای return استاندارد guard را برگرداند.
export const adminChildGuard: CanActivateChildFn = (
childRoute: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
) => {
const authService = inject(AuthService);
return authService.hasRole('admin');
};برای اطلاعات بیشتر، API docs مربوط به CanActivateChildFn را ببینید.
CanDeactivate
Guard مربوط به CanDeactivate مشخص میکند آیا کاربر میتواند از یک route خارج شود یا نه. یک سناریوی رایج، جلوگیری از navigation دور شدن از formهای ذخیرهنشده است.
به argumentهای پیشفرض زیر دسترسی دارد:
component:T- instance مربوط به componentای که در حال deactivated شدن استcurrentRoute:ActivatedRouteSnapshot- شامل اطلاعاتی درباره route فعلیcurrentState:RouterStateSnapshot- شامل router state فعلیnextState:RouterStateSnapshot- شامل router state بعدی که قرار است به آن navigate شود
میتواند نوعهای return استاندارد guard را برگرداند.
export const unsavedChangesGuard: CanDeactivateFn<Form> = (
component: Form,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState: RouterStateSnapshot,
) => {
return component.hasUnsavedChanges()
? confirm('You have unsaved changes. Are you sure you want to leave?')
: true;
};برای اطلاعات بیشتر، API docs مربوط به CanDeactivateFn را ببینید.
CanMatch
Guard مربوط به CanMatch مشخص میکند آیا یک route میتواند در طول path matching match شود یا نه. برخلاف guardهای دیگر، رد شدن آن باعث میشود router routeهای matching دیگر را امتحان کند، نه اینکه navigation را کاملا block کند. این قابلیت برای feature flagها، A/B testing، یا route loading شرطی مفید است.
به argumentهای پیشفرض زیر دسترسی دارد:
route:Route- route configurationای که در حال ارزیابی استsegments:UrlSegment[]- URL segmentهایی که توسط ارزیابی routeهای والد قبلی مصرف نشدهاندcurrentSnapshot: PartialMatchRouteSnapshot- route snapshot فعلی تا این نقطه از فرایند matching
میتواند نوعهای return استاندارد guard را برگرداند، اما وقتی false برگرداند، Angular بهجای block کردن کامل navigation، routeهای matching دیگر را امتحان میکند.
export const featureToggleGuard: CanMatchFn = (
route: Route,
segments: UrlSegment[],
currentSnapshot: PartialMatchRouteSnapshot,
) => {
const featureService = inject(FeatureService);
return featureService.isFeatureEnabled('newDashboard');
};همچنین میتواند به شما اجازه دهد برای یک path یکسان از componentهای متفاوت استفاده کنید.
// 📄 routes.ts
const routes: Routes = [
{
path: 'dashboard',
component: AdminDashboard,
canMatch: [adminGuard],
},
{
path: 'dashboard',
component: UserDashboard,
canMatch: [userGuard],
},
];در این مثال، وقتی کاربر /dashboard را باز کند، اولین routeای که guard درست آن match شود استفاده خواهد شد.
برای اطلاعات بیشتر، API docs مربوط به CanMatchFn را ببینید.
اعمال guardها روی routeها
بعد از اینکه route guardهای خود را ساختید، باید آنها را در route definitionهای خود configure کنید.
Guardها در route configuration بهصورت array مشخص میشوند تا بتوانید چند guard را روی یک route اعمال کنید. آنها به همان ترتیبی اجرا میشوند که در array آمدهاند.
import {Routes} from '@angular/router';
import {authGuard} from './guards/auth.guard';
import {adminGuard} from './guards/admin.guard';
import {canDeactivateGuard} from './guards/can-deactivate.guard';
import {featureToggleGuard} from './guards/feature-toggle.guard';
const routes: Routes = [
// Basic CanActivate - requires authentication
{
path: 'dashboard',
component: Dashboard,
canActivate: [authGuard],
},
// Multiple CanActivate guards - requires authentication AND admin role
{
path: 'admin',
component: Admin,
canActivate: [authGuard, adminGuard],
},
// CanActivate + CanDeactivate - protected route with unsaved changes check
{
path: 'profile',
component: Profile,
canActivate: [authGuard],
canDeactivate: [canDeactivateGuard],
},
// CanActivateChild - protects all child routes
{
path: 'users', // /user - NOT protected
canActivateChild: [authGuard],
children: [
// /users/list - PROTECTED
{path: 'list', component: UserList},
// /users/detail/:id - PROTECTED
{path: 'detail/:id', component: UserDetail},
],
},
// CanMatch - conditionally matches route based on feature flag
{
path: 'beta-feature',
component: BetaFeature,
canMatch: [featureToggleGuard],
},
// Fallback route if beta feature is disabled
{
path: 'beta-feature',
component: ComingSoon,
},
];