Angular HttpClient und Interceptoren 2026: Request-Handling und Interview-Fragen
Umfassender Leitfaden zu Angular HttpClient Interceptoren: funktionale Interceptoren, Authentifizierung, Token-Refresh, Caching und häufige Interview-Fragen für Angular-Entwickler.

Angular HttpClient Interceptoren verändern grundlegend, wie Anwendungen HTTP-Requests verarbeiten – von der Authentifizierung bis zur Retry-Logik. Angular 20 hat funktionale Interceptoren als empfohlenen Ansatz standardisiert und ersetzt damit das ältere klassenbasierte Muster durch eine vorhersagbarere und tree-shakable API.
Angular 20 empfiehlt funktionale Interceptoren mit withInterceptors() gegenüber klassenbasierten Interceptoren. Funktionale Interceptoren haben eine vorhersagbarere Ausführungsreihenfolge und integrieren sich besser mit Standalone-Komponenten.
HttpClient-Setup mit provideHttpClient
Bevor Interceptoren geschrieben werden können, muss HttpClient im Application-Bootstrap konfiguriert werden. Angulars Standalone-Architektur verwendet provideHttpClient() anstelle des älteren HttpClientModule.
Die Konfiguration erfolgt in der Application-Konfiguration:
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(
// Interceptoren werden in Array-Reihenfolge ausgeführt
withInterceptors([authInterceptor, loggingInterceptor])
),
],
});Interceptoren werden in der Reihenfolge ausgeführt, die im Array angegeben ist. Der erste Interceptor verarbeitet den ausgehenden Request zuerst, und der letzte Interceptor sieht die Response zuerst.
Anatomie eines funktionalen Interceptors
Ein funktionaler Interceptor erhält den Request und eine next-Funktion. Der Aufruf von next(req) leitet den Request an den nächsten Interceptor in der Kette weiter oder an das Backend, falls dies der letzte Interceptor ist.
Die grundlegende Struktur sieht folgendermaßen aus:
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);
},
})
);
};Der Interceptor protokolliert die Request-Methode und URL und misst dann die Response-Zeit. Der tap-Operator beobachtet die Response, ohne sie zu modifizieren.
Authentifizierungs-Interceptor mit Token-Injection
Die meisten Anwendungen müssen Authentifizierungs-Tokens an ausgehende Requests anhängen. Der Interceptor klont den Request mit dem Authorization-Header, da HttpRequest-Objekte unveränderlich sind.
Dieses Muster ruft das Token von einem Service ab und fügt es hinzu:
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
// Services mit Angulars inject()-Funktion injizieren
const authService = inject(AuthService);
const token = authService.getAccessToken();
// Auth-Header für öffentliche Endpoints überspringen
if (req.url.includes('/public/') || !token) {
return next(req);
}
// Request mit Authorization-Header klonen
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
return next(authReq);
};Die inject()-Funktion funktioniert, weil Interceptoren innerhalb von Angulars Injection-Kontext ausgeführt werden. Dies eliminiert den Constructor-Injection-Boilerplate klassenbasierter Interceptoren.
Fehlerbehandlung und Retry-Logik
Netzwerkfehler passieren. Ein Retry-Interceptor kann fehlgeschlagene Requests automatisch mit exponentiellem Backoff wiederholen und so die Zuverlässigkeit verbessern, ohne Änderungen an jedem HTTP-Aufruf zu erfordern.
RxJS-Operatoren übernehmen die Retry-Logik:
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { retry, timer } from 'rxjs';
export const retryInterceptor: HttpInterceptorFn = (req, next) => {
// Nur GET-Requests wiederholen (idempotent)
if (req.method !== 'GET') {
return next(req);
}
return next(req).pipe(
retry({
count: 3,
delay: (error: HttpErrorResponse, retryCount: number) => {
// Client-Fehler (4xx) nicht wiederholen
if (error.status >= 400 && error.status < 500) {
throw error;
}
// Exponentielles Backoff: 1s, 2s, 4s
const delayMs = Math.pow(2, retryCount - 1) * 1000;
console.log(`Retry #${retryCount} in ${delayMs}ms`);
return timer(delayMs);
},
})
);
};Der Interceptor wiederholt nur GET-Requests, da POST, PUT und DELETE nicht idempotent sind. Client-Fehler (4xx) überspringen Retries, da der Server den Request explizit abgelehnt hat.
Bereit für deine Angular-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Response-Caching mit HttpContext
HttpClient bietet HttpContext, um Metadaten zwischen Interceptoren und aufrufendem Code zu übergeben. Ein Caching-Interceptor kann Context-Tokens lesen, um das Cache-Verhalten zu bestimmen.
Das Cache-Token und der Interceptor arbeiten zusammen:
import { HttpInterceptorFn, HttpContextToken, HttpResponse } from '@angular/common/http';
import { of, tap } from 'rxjs';
// Context-Token zur Steuerung des Cachings pro Request
export const CACHE_REQUEST = new HttpContextToken<boolean>(() => false);
const cache = new Map<string, HttpResponse<unknown>>();
export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
// Nur cachen, wenn explizit via Context angefordert
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());
}
})
);
};Der aufrufende Code aktiviert Caching mit dem Context-Token:
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),
});
}
}Dieses Muster hält das Caching-Verhalten explizit an der Aufrufstelle, anstatt es in der globalen Konfiguration zu verbergen.
Token-Refresh mit 401-Handling
Wenn Access-Tokens ablaufen, gibt der Server eine 401-Response zurück. Ein Interceptor kann dies abfangen, das Token aktualisieren und den ursprünglichen Request transparent wiederholen.
Die Refresh-Logik koordiniert mehrere gleichzeitige Requests:
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) {
// Warten bis der Refresh abgeschlossen ist
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);
})
);
})
);
};Das BehaviorSubject verhindert mehrfache Refresh-Aufrufe, wenn mehrere Requests gleichzeitig fehlschlagen. Sobald das Token aktualisiert ist, wiederholen alle wartenden Requests mit dem neuen Token.
Interview-Fragen: HttpClient und Interceptoren
Technische Interviews testen oft das Verständnis von HTTP-Handling-Patterns. Diese Fragen erscheinen häufig bei Angular-Positionen.
F: Warum werden funktionale Interceptoren gegenüber klassenbasierten Interceptoren bevorzugt?
Funktionale Interceptoren haben eine vorhersagbarere Ausführungsreihenfolge. Die Angular-Dokumentation besagt, dass funktionale Interceptoren mit withInterceptors() eine strikte Array-Reihenfolge einhalten, während DI-basierte Interceptoren in komplexen Modul-Hierarchien Reihenfolgeprobleme haben können. Funktionale Interceptoren produzieren auch kleinere Bundles, da sie besser tree-shaken.
F: Wie verhindert man, dass ein Interceptor bei bestimmten Requests ausgeführt wird?
Es gibt zwei Ansätze. Erstens kann das URL-Muster im Interceptor geprüft und next(req) ohne Modifikation für ausgeschlossene Pfade aufgerufen werden. Zweitens können HttpContext-Tokens verwendet werden, die der aufrufende Code setzt, um sich vom Interceptor-Verhalten abzumelden. Der Context-Ansatz hält die Ausschlussentscheidung an der Aufrufstelle.
F: Was passiert, wenn man vergisst, next() in einem Interceptor aufzurufen?
Der Request erreicht nie den Server. Das Observable, das von next() zurückgegeben wird, repräsentiert den Rest der Interceptor-Kette und den tatsächlichen HTTP-Aufruf. Ohne dessen Aufruf sehen nachfolgende Interceptoren und das Backend den Request nie. Dies ist beabsichtigt für Caching-Interceptoren, die gecachte Responses zurückgeben.
F: Wie behandelt man gleichzeitige Requests während eines Token-Refresh?
Es wird ein BehaviorSubject zur Koordination verwendet. Wenn der erste 401 einen Refresh auslöst, wird ein Flag gesetzt und null auf dem Subject emittiert. Nachfolgende 401s warten mit filter auf dem Subject, bis das neue Token ankommt. Dies verhindert mehrfache Refresh-Aufrufe und stellt sicher, dass alle ausstehenden Requests mit demselben frischen Token wiederholt werden.
F: Können Interceptoren den Response-Body modifizieren?
Ja. map() wird auf dem Observable verwendet, das von next() zurückgegeben wird, um HttpResponse-Objekte zu transformieren. Da Responses unveränderlich sind, werden sie mit modifizierten Bodies geklont. Dieses Muster funktioniert zum Hinzufügen berechneter Felder, Auspacken von API-Envelopes oder Normalisieren von Datenstrukturen.
Für weitere Angular Interview-Vorbereitung siehe das HttpClient Interview-Fragen Modul.
Testen funktionaler Interceptoren
Interceptoren erfordern Tests mit gemockten HTTP-Handlern. Angulars HttpTestingController funktioniert mit Interceptoren, die über provideHttpClient() registriert wurden.
Ein Test-Setup sieht folgendermaßen aus:
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' });
});
});Der Test verifiziert sowohl die Header-Injection als auch die Ausnahme für öffentliche Endpoints, ohne tatsächliche Netzwerk-Requests zu machen.
Produktionsmuster: Kombination von Interceptoren
Echte Anwendungen stapeln mehrere Interceptoren. Die Reihenfolge ist wichtig: Authentifizierung sollte vor Logging laufen, und Caching sollte vor Retry-Logik laufen.
Eine Produktionskonfiguration könnte so aussehen:
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([
// Reihenfolge: auth -> refresh -> cache -> retry -> logging
authInterceptor,
tokenRefreshInterceptor,
cacheInterceptor,
retryInterceptor,
loggingInterceptor,
])
),
],
});Diese Reihenfolge stellt sicher, dass Tokens vor dem Refresh-Handling angehängt werden, Cache-Prüfungen vor Retries erfolgen und Logging das finale Timing erfasst.
Für umfassendere Angular-Patterns siehe das RxJS Grundlagen Modul, das die in Interceptoren verwendeten Operatoren behandelt.
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Wichtige Erkenntnisse zu Angular HttpClient Interceptoren
- Funktionale Interceptoren mit
withInterceptors()sind der Angular 20-Standard und bieten vorhersagbare Reihenfolge sowie besseres Tree-Shaking als klassenbasierte Alternativen inject()innerhalb von Interceptoren verwenden, um auf Services ohne Constructor-Boilerplate zuzugreifen- Requests mit
req.clone()klonen, um Header zu modifizieren, daHttpRequest-Objekte unveränderlich sind HttpContext-Tokens machen Request-spezifisches Verhalten explizit an der Aufrufstelle, anstatt es in der globalen Konfiguration zu verbergen- Token-Refresh-Interceptoren sollten gleichzeitige Requests mit
BehaviorSubjectkoordinieren, um mehrfache Refresh-Aufrufe zu verhindern - Die Interceptor-Reihenfolge ist wichtig: Authentifizierung vor Caching, Caching vor Retry, Logging zuletzt
Findest du den Bug in Angular?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 22. August 2026
Teilen
Verwandte Artikel

Angular Signals und Computed 2026: Fein-granulare Reaktivität und Interview-Fragen
Ein tiefgehender Leitfaden zu Angular Signals, Computed Signals und dem reaktiven Modell in Angular 20+. Enthält Best Practices, RxJS-Interoperabilität und häufige technische Interview-Fragen.

Fortgeschrittene Angular Dependency Injection 2026: Provider, Tokens und Interview-Fragen
Angular bietet mit Providern, InjectionTokens und hierarchischen Injektoren ein mächtiges DI-System. Diese tiefgehende Analyse behandelt Provider-Strategien, die inject()-Funktion und praxisrelevante Interview-Fragen.

Angular Control Flow Syntax 2026: @if, @for, @switch und Interviewfragen
Umfassende Anleitung zur neuen Angular Control Flow Syntax mit @if, @for und @switch. Performance-Optimierungen, Signal-Integration und häufige Interviewfragen für Angular-Entwickler.