Angular : ancien intercepteur avec classe (HttpInterceptor)

🏷️ Front-end 📅 13/04/2026 09:00:00 👤 Mezgani said
Angular Interceptor Http Httpclient Classe
Angular : ancien intercepteur avec classe (HttpInterceptor)

Comprenez le fonctionnement des intercepteurs Angular basés sur une classe : implémentation de HttpInterceptor, injection de dépendances, chaînage et cas d'usage courants.

Résumé : l'intercepteur basé sur une classe

Avant Angular 15, les intercepteurs HTTP étaient obligatoirement définis comme une classe injectable implémentant l'interface HttpInterceptor. Cette approche, introduite avec HttpClientModule dans Angular 4.3, est encore très présente dans les codebases Angular existants.

À retenir : L'ancien style avec classe fonctionne toujours dans Angular 20. Il n'est pas déprécié — mais le nouvel style fonctionnel est recommandé pour les nouvelles applications.

Fonctionnement général :

  • Un intercepteur intercepte toutes les requêtes HTTP sortantes et les réponses entrantes
  • Il peut modifier les requêtes, ajouter des headers, logger, gérer les erreurs
  • Les intercepteurs sont chaînés dans l'ordre d'enregistrement
  • Chaque intercepteur doit appeler next.handle(req) pour passer la requête au suivant

Implémenter HttpInterceptor

Un intercepteur classe implémente l'interface HttpInterceptor et sa méthode intercept().

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>> {

    // 1. Cloner la requête (immuable)
    const authReq = req.clone({
      setHeaders: {
        Authorization: `Bearer ${this.getToken()}`
      }
    });

    // 2. Passer la requête modifiée au prochain intercepteur
    return next.handle(authReq);
  }

  private getToken(): string {
    return localStorage.getItem('token') ?? '';
  }
}
Immuabilité : La requête HTTP est immuable. Pour la modifier, il faut toujours utiliser req.clone() qui retourne une nouvelle instance.

Enregistrement dans le module

L'intercepteur doit être enregistré avec le token HTTP_INTERCEPTORS dans le module principal, avec multi: true pour permettre plusieurs intercepteurs.

// app.module.ts
import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './interceptors/auth.interceptor';

@NgModule({
  imports: [HttpClientModule],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true  // indispensable pour permettre plusieurs intercepteurs
    }
  ]
})
export class AppModule {}

Pour les applications standalone (Angular 14+) avec l'ancien style :

// main.ts — app standalone avec intercepteur classe
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { HTTP_INTERCEPTORS } from '@angular/common/http';

bootstrapApplication(AppComponent, {
  providers: [
    provideHttpClient(withInterceptorsFromDi()),
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ]
});

Injection de dépendances dans un intercepteur

L'un des avantages de l'approche classe est l'accès facile à l'injection de dépendances via le constructeur.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

  // Injection via constructeur
  constructor(
    private router: Router,
    private authService: AuthService
  ) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error.status === 401) {
          this.authService.logout();
          this.router.navigate(['/login']);
        }
        if (error.status === 403) {
          this.router.navigate(['/forbidden']);
        }
        return throwError(() => error);
      })
    );
  }
}

Cas d'usage courants

1. Intercepteur de logging :

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const start = Date.now();
    console.info(`[HTTP] ${req.method} ${req.url}`);

    return next.handle(req).pipe(
      tap({
        next: (event) => {
          if (event instanceof HttpResponse) {
            console.info(`[HTTP] ${req.url} — ${Date.now() - start}ms`);
          }
        }
      })
    );
  }
}

2. Intercepteur d'en-tête de langue :

@Injectable()
export class LanguageInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const lang = localStorage.getItem('lang') || 'fr';
    return next.handle(req.clone({
      setHeaders: { 'Accept-Language': lang }
    }));
  }
}

Chaînage de plusieurs intercepteurs

L'ordre d'enregistrement dans le tableau providers définit l'ordre d'exécution : le premier enregistré est le premier exécuté pour les requêtes, et le dernier pour les réponses.

providers: [
  { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },     // 1er
  { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },  // 2ème
  { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },    // 3ème
]

// Ordre d'exécution pour une REQUÊTE :
// AuthInterceptor → LoggingInterceptor → ErrorInterceptor → Serveur

// Ordre d'exécution pour une RÉPONSE :
// Serveur → ErrorInterceptor → LoggingInterceptor → AuthInterceptor

Limites de l'approche classe

  • Verbeux : nécessite une classe complète, un décorateur @Injectable() et un enregistrement explicite
  • Injection circulaire : injecter HttpClient dans un intercepteur (ex: pour rafraîchir un token) peut causer des dépendances circulaires
  • Non tree-shakeable : les intercepteurs classe sont toujours inclus dans le bundle même s'ils ne sont pas utilisés dans certaines routes
  • Tests plus verbeux : nécessitent plus de setup dans TestBed
À retenir : Ces limitations ont motivé la création du nouveau style fonctionnel dans Angular 15. Pour les nouveaux projets, préférez HttpInterceptorFn — voir l'article sur le nouvel intercepteur fonctionnel.