В Angular защита — это фундаментальная функция, которая защищает маршруты внутри приложения. Охранники проверяют, выполняются ли определенные условия, прежде чем разрешить пользователям доступ к определенным маршрутам. Guards можно использовать для аутентификации и авторизации, управления доступом на основе ролей, проверки флагов функций и многого другого.
Angular предоставляет несколько типов охранников, которые можно использовать для разных целей, например, canActivate, canActivateChild, canDeactivate и canLoad. Эти охранники определяются как классы, которые реализуют определенные интерфейсы, предоставляемые инфраструктурой Angular. Когда защита добавляется в массив canActivate маршрута, она вызывается перед тем, как позволить пользователю перейти к желаемому маршруту.
В этом примере у нас есть маршрут Dashboard, для доступа к которому требуется роль администратора.
// routes.ts { path: 'dashboard', loadComponent: () => import('./pages/dashboard.component') },
Традиционный (класс) Router Guard
Чтобы предотвратить доступ пользователя без прав администратора к этому маршруту, мы можем использовать защиту класса, как показано ниже.
@Injectable({providedIn: 'root'}) export class AuthGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { return this.authService.isAdmin().pipe( tap(isAdmin => !isAdmin ? this.router.navigate(['/login']) : true) ) } }
Затем мы можем применить защиту к маршрутам.
{ path: 'dashboard', canActivate: [AuthGuard], loadComponent: () => import('./pages/dashboard.component') },
Этот старый подход работает, но на основе углового опроса:
Мы получили отзывы от нескольких разработчиков, которые говорят о том, что разработчики хотят меньше шаблонов и больше производительности.
Что привело к развитию функциональной защиты маршрутизатора в Angular v14.
Функциональные средства защиты маршрутизатора с
inject()
легкие, эргономичные и более компонуемые, чем средства защиты на основе классов.
Функциональная защита маршрутизатора с помощью функции inject()
Функция внедрения — это новая функция в Angular 14, которая помогает внедрять внешние зависимости в наши функции.
const authGuard = () => { const authService = inject(AuthService) const router = inject(Router) if (authService.isAdmin()) { return true } return router.navigate(['/login']) }
Регистрация функции функциональной защиты аналогична регистрации на основе класса.
// routes.ts { path: 'dashboard', canActivate: [authGuard], loadComponent: () => import('./pages/dashboard.component') },
Тестирование функциональной защиты
Тестирование защиты в Angular является важной частью процесса разработки, чтобы убедиться, что защита работает правильно и обеспечивает желаемую функциональность. Честно говоря, мне не удалось найти много упоминаний о тестировании inject() внутри функциональной защиты маршрутизатора. Вот пример, который работает для меня. Идея смоделировать inject()
внутри пакета @angular/core
.
import { authGuard } from './auth.guard' import * as angularCore from '@angular/core' // METHOD 1: Mocking inject() const isAdminMock= jest.fn() describe('AuthGuard', () => { beforeEach(() => { const injectSpy = jest.spyOn(angularCore, 'inject') injectSpy.mockImplementation((providerToken: unknown) => { if (providerToken === AuthService) { return { isAdmin: isAdminMock, } } }) }) afterEach(() => { jest.clearAllMocks() }) it('should return true when user is admin', () => { isAdminMock.mockReturnValue(true) expect(authGuard()).toBe(true) }) }) // METHOD 2: using TestBed describe('AuthGuard', () => { it('should return true', () => { TestBed.configureTestingModule({ providers: [ { provide: AuthService, useValue: { isAdmin: () => true }, }, ], }); const guard = TestBed.runInInjectionContext(authGuard); expect(guard).toBeTruthy(); }); });
Я надеюсь, что вы найдете этот пост полезным. Если у вас есть какие-либо улучшения, пожалуйста, оставьте комментарий :)