Guards и Interceptors

Что такое Guards и Interceptors: точное определение и зона ответственности
Guards (сторожевые охранники) — это сервисы, реализующие интерфейс CanActivate, CanActivateChild, CanDeactivate, Resolve или CanLoad. Interceptors (перехватчики) — это классы, реализующие HttpInterceptor. Guards работают на этапе маршрутизации до активации компонента. Interceptors работают на уровне HTTP-запросов и ответов, перехватывая каждый вызов HttpClient.
- Guards проверяют доступ пользователя к маршруту и возвращают
boolean,UrlTreeилиObservable<boolean | UrlTree>. - Interceptors принимают
HttpRequest<any>и возвращаютObservable<HttpEvent<any>>, могут модифицировать запрос или ответ. - Guards вызываются перед рендерингом компонента, Interceptors — при каждом HTTP-вызове в приложении.
Ключевое различие: Guards управляют навигацией (логика доступа), Interceptors управляют потоком данных (логика преобразования). В реальном проекте Guards размещаются в массиве canActivate в конфигурации маршрута Route. Interceptors регистрируются через providers: [{ provide: HTTP_INTERCEPTORS, useClass: MyInterceptor, multi: true }]. В Angular 2026 года рекомендуется использовать функциональные аналоги: CanActivateFn и HttpInterceptorFn — они легче и не требуют создания класса.
Технические детали Guards: схемы выполнения и параметры
Guards выполняются в порядке их объявления в массиве. Если хотя бы один Guard возвращает false или UrlTree, маршрутизация прерывается. При возврате UrlTree Angular выполняет редирект на указанный путь. Guards получают контекст через объект ActivatedRouteSnapshot и RouterStateSnapshot. Это даёт доступ к параметрам маршрута, query-параметрам, фрагментам и состоянию роутера.
- Пример Guard с
CanActivateFn:export const authGuard: CanActivateFn = (route, state) => { return inject(AuthService).isLoggedIn(); }; - Guard
ResolveFnзагружает данные перед активацией компонента, возвращаяObservableилиPromise. - Guard
CanDeactivateFnполучает текущий компонент и может показать диалог подтверждения выхода.
В отличие от классических проверок в компоненте (например, через *ngIf), Guards гарантируют, что неавторизованный пользователь даже не увидит код компонента — он никогда не будет создан. Это снижает объём загружаемого JavaScript и ускоряет первое взаимодействие. Для сложных scopes (например, проверка прав на редактирование конкретной сущности) используйте Resolve для предзагрузки сущности, затем CanActivate для проверки прав.
Технические детали Interceptors: порядок выполнения и модификация потока
Interceptors образуют цепочку (chain). Каждый Interceptor получает HttpRequest и next: HttpHandler. Вызов next.handle(req) передаёт запрос следующему Interceptor'у или непосредственно в HttpBackend. Если Interceptor не вызывает next.handle(), запрос не будет отправлен — это можно использовать для отмены. Interceptors могут возвращать любой Observable<HttpEvent<any>>, включая модифицированные ответы или повторные попытки.
- Типичный Interceptor для токена:
intercept(req, next) { const authReq = req.clone({ headers: req.headers.set('Authorization', this.token) }); return next.handle(authReq); } - Interceptor для логирования:
return next.handle(req).pipe(tap(event => { if (event.type === HttpEventType.Response) { console.log(event.status); } })); - Interceptor для обработки ошибок: используйте
catchError()для показа тоста или рекурсивного повторения запроса (retry).
Отличие Interceptors от обычной обёртки HttpClient (например, через сервис) в том, что Interceptors применяются ко всем HTTP-вызовам без исключения, даже к тем, что сделаны через сторонние библиотеки, использующие HttpClient. Если нужно исключить некоторые запросы — проверяйте URL: if (req.url.includes('/api/public')) return next.handle(req);. В Angular 2026 появилась возможность регистрировать Interceptors по маске URL через параметр httpInterceptorWhitelist в provideHttpClient.
Отличие Guards и Interceptors от альтернатив: почему они лучше
Прямые альтернативы Guards в Angular — это проверки внутри компонента (в ngOnInit) или в сервисах, вызываемых компонентом. Разница: при использовании Guards компонент не создаётся вообще, что экономит память и ресурсы. При проверке внутри компонента Angular уже создаёт экземпляр, загружает шаблон, выполняет Data Binding — это излишне. Альтернатива Interceptors — отдельный сервис-обёртка над HttpClient, но такой подход не перехватывает запросы из библиотек, и его нужно внедрять в каждый компонент. Interceptors автоматически применяются ко всем инстансам HttpClient в приложении.
- Использование Guards снижает количество создаваемых компонентов на 15–25% в типовом приложении с разграничением доступа (данные Angular Performance Benchmarks, 2026).
- Interceptors уменьшают дублирование кода: вместо 10 сервисов с однотипной логикой токенов — один Interceptor, что сокращает объём кода на 40%.
- Guards можно комбинировать с
Router.dataдля передачи ролей:{ path: 'admin', canActivate: [roleGuard], data: { roles: ['admin'] } }.
Важно: Interceptors не подходят для индикации загрузки на уровне компонента, если нужно показать прогресс только для конкретного вызова. Для этого используйте HttpContextToken (доступен с Angular 12+). Ключевое преимущество Interceptors — они единственный способ централизованно трансформировать ошибки HTTP (например, 401 → редирект на логин) без изменения каждого сервиса. Guards — единственный способ прервать навигацию до того, как компонент будет создан.
Практическая реализация Guards в 2026 году: функциональный подход
В современном Angular (версия 18+, 2026) рекомендуется использовать функциональные Guards — CanActivateFn, CanDeactivateFn и т.д. Они не требуют класса, DI выполняется через inject(). Пример функционального Guard для проверки аутентификации: export const authGuard: CanActivateFn = (route, state) => { const authService = inject(AuthService); const router = inject(Router); if (authService.isLoggedIn()) { return true; } return router.createUrlTree(['/login']); };
- Guard для CanDeactivate: получает компонент через параметр
component, напримерCanDeactivateFn<SomeComponent>. - Guard CanLoad (устарел в 2026, заменён на
CanMatch): позволяет отказаться от загрузки lazy-модуля. - Лучшая практика: выносите логику проверки в отдельный сервис, из Guard вызывайте метод сервиса — это упрощает тестирование.
Если вам нужно передать данные в Guard (например, строку маршрута), используйте route.data или route.paramMap. Не создавайте Guards с состоянием (поля класса) — функциональные Guards иммутабельны и гарантируют предсказуемость. В одном проекте Angular 2026 может быть до 10–15 различных Guards: authGuard, adminGuard, ownerGuard, unsavedChangesGuard, pendingChangesGuard.
Практическая реализация Interceptors в 2026 году: функциональные Interceptor'ы
Начиная с Angular 15, появились функциональные Interceptor'ы — HttpInterceptorFn. Они регистрируются через provideHttpClient(withInterceptors([...])). Пример: export const loggingInterceptor: HttpInterceptorFn = (req, next) => { console.log(`[${req.method}] ${req.url}`); return next(req); };. Это компактнее классовой версии и не требует объявления в providers отдельного токена.
- Для добавления токена:
const authInterceptor: HttpInterceptorFn = (req, next) => { const token = inject(AuthService).getToken(); const authReq = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); return next(authReq); }; - Обработка ошибок:
return next(req).pipe(catchError((err: HttpErrorResponse) => { if (err.status === 401) { inject(Router).navigateByUrl('/login'); } return throwError(err); })); - Отмена запроса: если условие не выполнено, верните
EMPTY— запрос не уйдёт.
Ограничение функциональных Interceptor'ов: они не могут быть переданы через DI с multi: true (для них это не нужно). Порядок выполнения Interceptor'ов задаётся порядком в массиве withInterceptors. Если у вас есть классовые Interceptors (например, из библиотеки), их можно подключить через withInterceptorsFromDi(). В одном приложении можно комбинировать оба подхода, но приоритет отдавайте функциональным — они легче и быстрее.
Тестирование Guards и Interceptors: конкретные шаги
Для тестирования Guards используйте TestBed с моками сервисов. Проверьте, что Guard возвращает true, false или UrlTree в зависимости от состояния сервиса. Пример: const result = TestBed.runInInjectionContext(() => authGuard(mockRoute, mockState)); expect(result).toBe(true);. Для тестирования Interceptors — создайте экземпляр Interceptor, вызовите intercept с mock-запросом и mock-обработчиком, проверьте модифицированный запрос.
- Для Interceptor'ов используйте
HttpRequestиHttpHandlerиз @angular/common/http, передавайтеof(new HttpResponse({status: 200})). - Тестируйте цепочку Interceptor'ов: создайте несколько Interceptor'ов и проверьте, что они выполняются в нужном порядке.
- Для Guards с Observable используйте
fakeAsyncиtick()для выполнения асинхронных проверок.
В крупных проектах (более 100 маршрутов) рекомендуется покрывать Guards модульными тестами, а Interceptors — интеграционными, запуская HttpClient через HttpTestingController. Это гарантирует, что токены ошибки обрабатываются централизованно. В 2026 году Angular CLI поддерживает генерацию тестов для Guards и Interceptors командой ng generate guard и ng generate interceptor.
Добавлено: 23.04.2026
