Angular HttpClient e Interceptor nel 2026: Gestione delle Richieste e Domande di Colloquio
Guida completa agli interceptor Angular HttpClient: interceptor funzionali, autenticazione, refresh dei token, caching e domande frequenti nei colloqui tecnici Angular.

Gli interceptor Angular HttpClient trasformano il modo in cui le applicazioni gestiscono le richieste HTTP, dall'aggiunta di header di autenticazione all'implementazione della logica di retry. Angular 20 ha standardizzato gli interceptor funzionali come approccio raccomandato, sostituendo il vecchio pattern basato su classi con un'API più prevedibile e ottimizzata per il tree-shaking.
Angular 20 raccomanda gli interceptor funzionali con withInterceptors() rispetto a quelli basati su classi. Gli interceptor funzionali hanno un ordine di esecuzione più prevedibile e si integrano meglio con i componenti standalone.
Configurazione di HttpClient con provideHttpClient
Prima di scrivere interceptor, HttpClient deve essere configurato nel bootstrap dell'applicazione. L'architettura standalone di Angular utilizza provideHttpClient() al posto del vecchio HttpClientModule.
La configurazione avviene nella configurazione dell'applicazione:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { AppComponent } from './app/app.component';
import { authInterceptor } from './interceptors/auth.interceptor';
import { loggingInterceptor } from './interceptors/logging.interceptor';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(
// Gli interceptor vengono eseguiti nell'ordine dell'array
withInterceptors([authInterceptor, loggingInterceptor])
),
],
});Gli interceptor vengono eseguiti nell'ordine specificato nell'array. Il primo interceptor elabora la richiesta in uscita per primo, e l'ultimo interceptor vede la risposta per primo.
Anatomia di un Interceptor Funzionale
Un interceptor funzionale riceve la richiesta e una funzione next. Chiamare next(req) passa la richiesta al prossimo interceptor nella catena o al backend se questo è l'ultimo interceptor.
La struttura base appare così:
import { HttpInterceptorFn, HttpRequest, HttpHandlerFn, HttpEvent } from '@angular/common/http';
import { Observable, tap } from 'rxjs';
export const loggingInterceptor: HttpInterceptorFn = (
req: HttpRequest<unknown>,
next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
const startTime = performance.now();
console.log(`[HTTP] ${req.method} ${req.url}`);
return next(req).pipe(
tap({
next: () => {
const duration = Math.round(performance.now() - startTime);
console.log(`[HTTP] ${req.method} ${req.url} completed in ${duration}ms`);
},
error: (err) => {
console.error(`[HTTP] ${req.method} ${req.url} failed:`, err.message);
},
})
);
};L'interceptor registra il metodo della richiesta e l'URL, quindi misura il tempo di risposta. L'operatore tap osserva la risposta senza modificarla.
Interceptor di Autenticazione con Injection del Token
La maggior parte delle applicazioni deve allegare token di autenticazione alle richieste in uscita. L'interceptor clona la richiesta con l'header Authorization, poiché gli oggetti HttpRequest sono immutabili.
Questo pattern recupera il token da un service e lo allega:
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
// Inietta i service usando la funzione inject() di Angular
const authService = inject(AuthService);
const token = authService.getAccessToken();
// Salta l'header auth per gli endpoint pubblici
if (req.url.includes('/public/') || !token) {
return next(req);
}
// Clona la richiesta con l'header Authorization
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
return next(authReq);
};La funzione inject() funziona perché gli interceptor vengono eseguiti all'interno del contesto di injection di Angular. Questo elimina il boilerplate dell'injection via constructor degli interceptor basati su classi.
Gestione degli Errori e Logica di Retry
I fallimenti di rete accadono. Un interceptor di retry può automaticamente riprovare le richieste fallite con backoff esponenziale, migliorando l'affidabilità senza richiedere modifiche a ogni chiamata HTTP.
Gli operatori RxJS gestiscono la logica di retry:
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { retry, timer } from 'rxjs';
export const retryInterceptor: HttpInterceptorFn = (req, next) => {
// Riprova solo le richieste GET (idempotenti)
if (req.method !== 'GET') {
return next(req);
}
return next(req).pipe(
retry({
count: 3,
delay: (error: HttpErrorResponse, retryCount: number) => {
// Non riprovare errori client (4xx)
if (error.status >= 400 && error.status < 500) {
throw error;
}
// Backoff esponenziale: 1s, 2s, 4s
const delayMs = Math.pow(2, retryCount - 1) * 1000;
console.log(`Retry #${retryCount} in ${delayMs}ms`);
return timer(delayMs);
},
})
);
};L'interceptor riprova solo le richieste GET perché POST, PUT e DELETE non sono idempotenti. Gli errori client (4xx) saltano i retry poiché il server ha esplicitamente rifiutato la richiesta.
Pronto a superare i tuoi colloqui su Angular?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Caching delle Risposte con HttpContext
HttpClient fornisce HttpContext per passare metadati tra interceptor e codice chiamante. Un interceptor di caching può leggere i token di contesto per determinare il comportamento della cache.
Il token della cache e l'interceptor lavorano insieme:
import { HttpInterceptorFn, HttpContextToken, HttpResponse } from '@angular/common/http';
import { of, tap } from 'rxjs';
// Token di contesto per controllare il caching per ogni richiesta
export const CACHE_REQUEST = new HttpContextToken<boolean>(() => false);
const cache = new Map<string, HttpResponse<unknown>>();
export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
// Mette in cache solo se esplicitamente richiesto via contesto
if (!req.context.get(CACHE_REQUEST) || req.method !== 'GET') {
return next(req);
}
const cacheKey = req.urlWithParams;
const cachedResponse = cache.get(cacheKey);
if (cachedResponse) {
console.log(`[Cache] HIT: ${cacheKey}`);
return of(cachedResponse.clone());
}
return next(req).pipe(
tap((event) => {
if (event instanceof HttpResponse) {
console.log(`[Cache] STORE: ${cacheKey}`);
cache.set(cacheKey, event.clone());
}
})
);
};Il codice chiamante attiva il caching con il token di contesto:
import { HttpClient, HttpContext } from '@angular/common/http';
import { CACHE_REQUEST } from '../interceptors/cache.interceptor';
export class UserService {
constructor(private http: HttpClient) {}
getUser(id: string) {
return this.http.get(`/api/users/${id}`, {
context: new HttpContext().set(CACHE_REQUEST, true),
});
}
}Questo pattern mantiene il comportamento del caching esplicito nel punto di chiamata piuttosto che nascosto nella configurazione globale.
Refresh del Token con Gestione del 401
Quando i token di accesso scadono, il server restituisce una risposta 401. Un interceptor può intercettare questo, aggiornare il token e riprovare la richiesta originale in modo trasparente.
La logica di refresh coordina più richieste concorrenti:
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { catchError, switchMap, throwError, BehaviorSubject, filter, take } from 'rxjs';
import { AuthService } from '../services/auth.service';
let isRefreshing = false;
const refreshTokenSubject = new BehaviorSubject<string | null>(null);
export const tokenRefreshInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status !== 401 || req.url.includes('/auth/refresh')) {
return throwError(() => error);
}
if (isRefreshing) {
// Aspetta che il refresh sia completato
return refreshTokenSubject.pipe(
filter((token) => token !== null),
take(1),
switchMap((token) => {
const retryReq = req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
});
return next(retryReq);
})
);
}
isRefreshing = true;
refreshTokenSubject.next(null);
return authService.refreshToken().pipe(
switchMap((response) => {
isRefreshing = false;
refreshTokenSubject.next(response.accessToken);
const retryReq = req.clone({
setHeaders: { Authorization: `Bearer ${response.accessToken}` },
});
return next(retryReq);
}),
catchError((refreshError) => {
isRefreshing = false;
authService.logout();
return throwError(() => refreshError);
})
);
})
);
};Il BehaviorSubject previene chiamate di refresh multiple quando diverse richieste falliscono simultaneamente. Una volta che il token viene aggiornato, tutte le richieste in coda riprovano con il nuovo token.
Domande di Colloquio: HttpClient e Interceptor
I colloqui tecnici spesso verificano la comprensione dei pattern di gestione HTTP. Queste domande appaiono frequentemente nelle posizioni Angular.
D: Perché gli interceptor funzionali sono preferiti rispetto a quelli basati su classi?
Gli interceptor funzionali hanno un ordine di esecuzione più prevedibile. La documentazione Angular afferma che gli interceptor funzionali con withInterceptors() mantengono un ordine di array rigoroso, mentre gli interceptor basati su DI possono avere problemi di ordinamento in gerarchie di moduli complesse. Gli interceptor funzionali producono anche bundle più piccoli perché sono meglio ottimizzati per il tree-shaking.
D: Come si impedisce a un interceptor di essere eseguito su richieste specifiche?
Esistono due approcci. Primo, controllare il pattern URL nell'interceptor e chiamare next(req) senza modifiche per i percorsi esclusi. Secondo, usare token HttpContext che il codice chiamante imposta per disattivare il comportamento dell'interceptor. L'approccio context mantiene la decisione di esclusione nel punto di chiamata.
D: Cosa succede se si dimentica di chiamare next() in un interceptor?
La richiesta non raggiunge mai il server. L'Observable restituito da next() rappresenta il resto della catena di interceptor e la chiamata HTTP effettiva. Senza chiamarlo, gli interceptor successivi e il backend non vedono mai la richiesta. Questo è intenzionale per gli interceptor di caching che restituiscono risposte dalla cache.
D: Come si gestiscono richieste concorrenti durante il refresh del token?
Si usa un BehaviorSubject per coordinare. Quando il primo 401 innesca un refresh, si imposta un flag e si emette null sul subject. I successivi 401 aspettano sul subject con filter finché non arriva il nuovo token. Questo previene chiamate di refresh multiple e assicura che tutte le richieste in attesa riprovino con lo stesso token fresco.
D: Gli interceptor possono modificare il corpo della risposta?
Sì. Si usa map() sull'Observable restituito da next() per trasformare gli oggetti HttpResponse. Poiché le risposte sono immutabili, vengono clonate con corpi modificati. Questo pattern funziona per aggiungere campi calcolati, spacchettare envelope API o normalizzare strutture dati.
Per ulteriore preparazione ai colloqui Angular, vedere il modulo domande colloquio HttpClient.
Test degli Interceptor Funzionali
Gli interceptor richiedono test con handler HTTP mockati. L'HttpTestingController di Angular funziona con interceptor registrati tramite provideHttpClient().
Una configurazione di test appare così:
import { TestBed } from '@angular/core/testing';
import { HttpClient, provideHttpClient, withInterceptors } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { authInterceptor } from './auth.interceptor';
import { AuthService } from '../services/auth.service';
describe('authInterceptor', () => {
let httpClient: HttpClient;
let httpTesting: HttpTestingController;
let authServiceSpy: jasmine.SpyObj<AuthService>;
beforeEach(() => {
authServiceSpy = jasmine.createSpyObj('AuthService', ['getAccessToken']);
TestBed.configureTestingModule({
providers: [
provideHttpClient(withInterceptors([authInterceptor])),
provideHttpClientTesting(),
{ provide: AuthService, useValue: authServiceSpy },
],
});
httpClient = TestBed.inject(HttpClient);
httpTesting = TestBed.inject(HttpTestingController);
});
it('should add Authorization header when token exists', () => {
authServiceSpy.getAccessToken.and.returnValue('test-token');
httpClient.get('/api/data').subscribe();
const req = httpTesting.expectOne('/api/data');
expect(req.request.headers.get('Authorization')).toBe('Bearer test-token');
req.flush({ data: 'test' });
});
it('should skip Authorization for public endpoints', () => {
authServiceSpy.getAccessToken.and.returnValue('test-token');
httpClient.get('/public/health').subscribe();
const req = httpTesting.expectOne('/public/health');
expect(req.request.headers.has('Authorization')).toBeFalse();
req.flush({ status: 'ok' });
});
});Il test verifica sia l'injection dell'header che l'eccezione per gli endpoint pubblici senza effettuare richieste di rete reali.
Pattern di Produzione: Combinare gli Interceptor
Le applicazioni reali impilano più interceptor. L'ordine conta: l'autenticazione dovrebbe essere eseguita prima del logging, e il caching dovrebbe essere eseguito prima della logica di retry.
Una configurazione di produzione potrebbe apparire così:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { AppComponent } from './app/app.component';
import { authInterceptor } from './interceptors/auth.interceptor';
import { tokenRefreshInterceptor } from './interceptors/token-refresh.interceptor';
import { cacheInterceptor } from './interceptors/cache.interceptor';
import { retryInterceptor } from './interceptors/retry.interceptor';
import { loggingInterceptor } from './interceptors/logging.interceptor';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(
withInterceptors([
// Ordine: auth -> refresh -> cache -> retry -> logging
authInterceptor,
tokenRefreshInterceptor,
cacheInterceptor,
retryInterceptor,
loggingInterceptor,
])
),
],
});Questo ordinamento assicura che i token vengano allegati prima della gestione del refresh, i controlli della cache avvengano prima dei retry, e il logging catturi il timing finale.
Per pattern Angular più ampi, vedere il modulo fondamentali RxJS che copre gli operatori usati negli interceptor.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Punti Chiave sugli Interceptor Angular HttpClient
- Gli interceptor funzionali con
withInterceptors()sono lo standard Angular 20, offrendo ordinamento prevedibile e migliore tree-shaking rispetto alle alternative basate su classi - Usare
inject()all'interno degli interceptor per accedere ai service senza boilerplate del constructor - Clonare le richieste con
req.clone()per modificare gli header poiché gli oggettiHttpRequestsono immutabili - I token
HttpContextrendono esplicito il comportamento per-richiesta nel punto di chiamata piuttosto che nascosto nella configurazione globale - Gli interceptor di refresh token dovrebbero coordinare richieste concorrenti con
BehaviorSubjectper prevenire chiamate di refresh multiple - L'ordine degli interceptor conta: autenticazione prima del caching, caching prima del retry, logging per ultimo
Sapresti trovare il bug in Angular?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 22 agosto 2026
Condividi
Articoli correlati

Angular Signals e Computed nel 2026: Reattività Fine-Grained e Domande per Colloqui
Una guida approfondita ad Angular Signals, Computed Signals e al modello reattivo in Angular 20+. Include best practice, interoperabilità con RxJS e domande frequenti nei colloqui tecnici.

Dependency Injection Avanzata in Angular 2026: Provider, Token e Domande da Colloquio
Il sistema di dependency injection di Angular offre un controllo preciso sull'istanziazione dei servizi attraverso provider, token e iniettori gerarchici. Questa analisi approfondita esplora le strategie dei provider, la funzione inject() e le domande tecniche da colloquio.

Angular Control Flow Syntax 2026: @if, @for, @switch e Domande da Colloquio
Guida completa alla nuova sintassi Control Flow di Angular con @if, @for e @switch. Ottimizzazioni delle performance, integrazione con i Signal e domande frequenti nei colloqui tecnici.