1. Знакомство с HTTP Interceptor
Давайте начнём с аналогии. Представьте себе почтовое отделение. Каждый раз, когда вы отправляете письмо (HTTP-запрос), оно сначала попадает к секретарю (Interceptor), который может проверить его, добавить штамп или даже завернуть письмо обратно, если оно не соответствует правилам. Аналогично, когда приходит ответ, секретарь тоже может его перехватить, например, проверить подпись или вложить дополнительную инструкцию.
HTTP Interceptor — это специальный класс в Angular, который перехватывает все ваши HTTP-запросы и ответы, проходящие через HttpClient. Он позволяет:
- Автоматически добавлять заголовки (например, токен авторизации) ко всем запросам.
- Централизованно обрабатывать ошибки (например, если сервер вернул 401 Unauthorized).
- Логировать все HTTP-запросы и ответы для отладки.
- Модифицировать тело запроса/ответа, если это необходимо.
- Реализовать повторные попытки (retry) при ошибках или показывать спиннер загрузки.
Всё это можно делать, не трогая код в каждом компоненте или сервисе! Interceptor — это как “единая точка входа” для всей сетевой активности вашего приложения.
Как работает Interceptor в Angular
В Angular Interceptor — это просто сервис, реализующий интерфейс HttpInterceptor. Вы реализуете метод intercept, который получает исходный запрос и следующий обработчик (next), а возвращает Observable с ответом.
Схематично:
Component/Service
↓
Interceptor 1
↓
Interceptor 2
↓
... (ещё interceptors)
↓
HttpClient (отправляет запрос на сервер)
↓
... (ответ проходит обратно через interceptors)
↓
Component/Service
Все interceptors складываются в цепочку (chain), и каждый может модифицировать запрос или ответ.
2. Простейший пример Interceptor: логирование запросов
Давайте напишем свой первый Interceptor, который будет просто логировать все HTTP-запросы и ответы.
Шаг 1: Генерируем Interceptor через Angular CLI
ng generate interceptor logging
Это создаст файл logging.interceptor.ts с заготовкой класса.
Шаг 2: Реализуем интерфейс HttpInterceptor
Вот базовый код для логирующего Interceptor:
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable, tap } from 'rxjs';
@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('[LoggingInterceptor] Отправка запроса:', req.url, req);
// Передаём запрос дальше по цепочке
return next.handle(req).pipe(
tap({
next: (event) => {
console.log('[LoggingInterceptor] Получен ответ:', event);
},
error: (err) => {
console.error('[LoggingInterceptor] Ошибка:', err);
}
})
);
}
}
Что тут происходит:
- Перед отправкой запроса мы выводим его в консоль.
- После получения ответа (или ошибки) — тоже логируем.
- next.handle(req) передаёт запрос дальше (либо следующему interceptor, либо уже на сервер).
Шаг 3: Регистрируем Interceptor в приложении
Вам нужно добавить Interceptor в массив провайдеров, чтобы Angular его использовал:
// app.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { LoggingInterceptor } from './logging.interceptor';
@NgModule({
// ...
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true }
]
})
export class AppModule { }
Ключевой момент: используем multi: true, чтобы Angular мог использовать несколько interceptors одновременно.
Теперь все запросы, проходящие через HttpClient, будут логироваться этим Interceptor.
3. Пример: Добавление заголовка авторизации
Частая задача — автоматически подставлять токен авторизации ко всем запросам, если пользователь залогинен. Для этого Interceptor — идеальный инструмент.
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Обычно токен берут из сервиса авторизации
const authToken = localStorage.getItem('auth_token');
// Клонируем запрос и добавляем заголовок, если токен есть
let authReq = req;
if (authToken) {
authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${authToken}`
}
});
}
// Передаём дальше
return next.handle(authReq);
}
}
Обратите внимание:
В Angular объекты HttpRequest иммутабельны — их нельзя менять напрямую, только клонировать с нужными изменениями через .clone().
4. Пример: Централизованная обработка ошибок
Хотите, чтобы при ошибке 401 пользователя сразу выкидывало на страницу логина, а при 500 — показывалось дружелюбное сообщение? Interceptor снова поможет!
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, catchError, throwError } from 'rxjs';
import { Router } from '@angular/router';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private router: Router) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
// Неавторизован — редиректим на логин
this.router.navigate(['/login']);
} else if (error.status === 500) {
alert('На сервере произошла ошибка. Попробуйте позже!');
}
// Пробрасываем ошибку дальше
return throwError(() => error);
})
);
}
}
5. Несколько Interceptors: порядок и best practices
Можно зарегистрировать сколько угодно interceptors. Они будут вызваны в том порядке, в каком вы их указали в массиве providers.
Например:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
]
Порядок важен!
- Сначала AuthInterceptor добавит токен.
- Потом LoggingInterceptor залогирует уже модифицированный запрос.
- Потом ErrorInterceptor обработает возможные ошибки.
Совет:
- Не делайте тяжелых синхронных вычислений или долгих асинхронных операций в interceptor — это может замедлить все HTTP-запросы.
- Не забывайте вызывать next.handle(req)! Если забыть — запрос никогда не уйдёт на сервер, и вы получите "тишину в эфире".
6. Типичные ошибки при работе с HTTP Interceptors
Ошибка №1: Не зарегистрировали interceptor или забыли multi: true.
В этом случае Angular возьмёт только последний interceptor, остальные проигнорирует. Всегда используйте multi: true.
Ошибка №2: Не вызвали next.handle(req).
Если забыли вызвать этот метод, запрос не пойдёт дальше по цепочке и не дойдёт до сервера. Это как забыть отправить письмо после того, как вы его написали.
Ошибка №3: Изменяете объект запроса напрямую.
Объекты HttpRequest иммутабельны! Используйте .clone() для любых изменений.
Ошибка №4: Слишком сложная логика или тяжёлые вычисления в interceptor.
Interceptor должен быть лёгким и быстрым — не делайте там долгих запросов к серверу или сложных вычислений.
Ошибка №5: Порядок регистрации interceptors не соответствует ожиданиям.
Запросы проходят через interceptors в том порядке, в каком они объявлены, а ответы — в обратном. Если порядок важен (например, логировать уже с добавленным токеном), расставьте interceptors правильно.
Ошибка №6: Не пробрасываете ошибку дальше (throwError).
Если вы “глотаете” ошибку в interceptor, компонент никогда не узнает, что запрос завершился неудачей.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ