Перехватчики HTTP
Перехватчик (interceptor) — функция, через которую проходит каждый запрос HttpClient перед отправкой и каждый ответ после получения. В нём делают то, что нужно всем запросам сразу: добавляют токен авторизации, пишут журнал, показывают общий индикатор загрузки.
Создание перехватчика
ng generate interceptor authCLI создаст файл src/app/auth-interceptor.ts (в версиях до 20 — auth.interceptor.ts) с функцией authInterceptor. Добавим в каждый запрос заголовок авторизации:
// src/app/auth-interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authReq = req.clone({
setHeaders: { Authorization: 'Bearer demo-token' },
});
return next(authReq);
};Функция получает запрос req и функцию next, которая передаёт запрос дальше. Запрос неизменяемый, поэтому его копируют методом clone() с нужными изменениями.
Регистрация
Перехватчик начинает работать после того, как его перечислят в withInterceptors():
// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './auth-interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor])),
],
};Откройте список статей и вкладку Network в инструментах разработчика: в заголовках запроса к jsonplaceholder.typicode.com появится Authorization: Bearer demo-token. Код сервисов и компонентов при этом не менялся.
Работа с ответом
next() возвращает Observable с ответом, к нему можно добавить операторы. Перехватчик-журнал измеряет время запроса:
// src/app/logging-interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { tap } from 'rxjs';
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
const started = Date.now();
return next(req).pipe(
tap({
complete: () => console.log(`${req.method} ${req.url}: ${Date.now() - started} мс`),
}),
);
};В консоли браузера после каждого запроса появится строка вроде GET https://jsonplaceholder.typicode.com/posts: 180 мс.
Несколько перехватчиков
// src/app/app.config.ts
provideHttpClient(withInterceptors([authInterceptor, loggingInterceptor])),Запрос проходит перехватчики в порядке массива, а ответ возвращается в обратном порядке.
Внедрение сервисов
Внутри перехватчика можно вызывать inject() — например, чтобы взять токен из сервиса авторизации, а не писать его в коде:
// src/app/auth-interceptor.ts
import { inject } from '@angular/core';
import { HttpInterceptorFn } from '@angular/common/http';
import { Auth } from './auth';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(Auth).token();
if (!token) {
return next(req);
}
return next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
};Сервис Auth с сигналом token мы сделаем в уроке о защитниках маршрутов.
Частая ошибка
Изменять запрос напрямую: req.headers.set('Authorization', ...). Метод set() возвращает новый объект заголовков, а старый запрос остаётся без изменений — заголовок не уйдёт на сервер. Всегда используйте req.clone() и передавайте в next() копию.