Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Перехватчики HTTP

Перехватчик (interceptor) — функция, через которую проходит каждый запрос HttpClient перед отправкой и каждый ответ после получения. В нём делают то, что нужно всем запросам сразу: добавляют токен авторизации, пишут журнал, показывают общий индикатор загрузки.

Создание перехватчика

ng generate interceptor auth

CLI создаст файл 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() копию.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.