Angular HttpClient i Interceptory w 2026: Obsługa Żądań HTTP i Pytania Rekrutacyjne
Kompletny przewodnik po funkcjonalnych interceptorach Angular 20, od wstrzykiwania tokenów uwierzytelniających po logikę ponawiania żądań z wykładniczym opóźnieniem. Zawiera pytania rekrutacyjne i wzorce produkcyjne.

Interceptory Angular HttpClient transformują sposób, w jaki aplikacje obsługują żądania HTTP — od dodawania nagłówków uwierzytelniających po implementację logiki ponawiania. Angular 20 ustandaryzował funkcjonalne interceptory jako zalecane podejście, zastępując starszy wzorzec oparty na klasach bardziej przewidywalnym i lepiej optymalizowalnym API.
Angular 20 rekomenduje funkcjonalne interceptory z withInterceptors() zamiast interceptorów opartych na klasach. Funkcjonalne interceptory mają bardziej przewidywalną kolejność wykonywania i lepiej integrują się z komponentami standalone.
Konfiguracja HttpClient z provideHttpClient
Przed napisaniem interceptorów należy skonfigurować HttpClient w bootstrapie aplikacji. Architektura standalone Angulara używa provideHttpClient() zamiast przestarzałego HttpClientModule.
Konfiguracja odbywa się w konfiguracji aplikacji:
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(
// Interceptory wykonują się w kolejności tablicy
withInterceptors([authInterceptor, loggingInterceptor])
),
],
});Interceptory wykonują się w kolejności określonej w tablicy. Pierwszy interceptor przetwarza wychodzące żądanie jako pierwszy, a ostatni interceptor jako pierwszy widzi odpowiedź.
Anatomia Funkcjonalnego Interceptora
Funkcjonalny interceptor otrzymuje żądanie i funkcję next. Wywołanie next(req) przekazuje żądanie do następnego interceptora w łańcuchu lub do backendu, jeśli jest to ostatni interceptor.
Podstawowa struktura wygląda następująco:
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);
},
})
);
};Interceptor loguje metodę żądania i URL, a następnie mierzy czas odpowiedzi. Operator tap obserwuje odpowiedź bez jej modyfikowania.
Interceptor Uwierzytelniania z Wstrzykiwaniem Tokena
Większość aplikacji wymaga dołączenia tokenów uwierzytelniających do wychodzących żądań. Interceptor klonuje żądanie z nagłówkiem Authorization, ponieważ obiekty HttpRequest są niemutowalne.
Ten wzorzec pobiera token z serwisu i dołącza go:
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
// Wstrzykiwanie serwisów za pomocą funkcji inject()
const authService = inject(AuthService);
const token = authService.getAccessToken();
// Pomijanie nagłówka auth dla publicznych endpointów
if (req.url.includes('/public/') || !token) {
return next(req);
}
// Klonowanie żądania z nagłówkiem Authorization
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
return next(authReq);
};Funkcja inject() działa, ponieważ interceptory wykonują się w kontekście wstrzykiwania zależności Angulara. Eliminuje to szablonowy kod konstruktora z interceptorów opartych na klasach.
Obsługa Błędów i Logika Ponawiania
Błędy sieciowe się zdarzają. Interceptor ponawiania może automatycznie ponawiać nieudane żądania z wykładniczym opóźnieniem, poprawiając niezawodność bez konieczności modyfikacji każdego wywołania HTTP.
Operatory RxJS obsługują logikę ponawiania:
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { retry, timer } from 'rxjs';
export const retryInterceptor: HttpInterceptorFn = (req, next) => {
// Ponawianie tylko żądań GET (idempotentnych)
if (req.method !== 'GET') {
return next(req);
}
return next(req).pipe(
retry({
count: 3,
delay: (error: HttpErrorResponse, retryCount: number) => {
// Nie ponawiaj błędów klienta (4xx)
if (error.status >= 400 && error.status < 500) {
throw error;
}
// Wykładnicze opóźnienie: 1s, 2s, 4s
const delayMs = Math.pow(2, retryCount - 1) * 1000;
console.log(`Ponowna próba #${retryCount} za ${delayMs}ms`);
return timer(delayMs);
},
})
);
};Interceptor ponawia tylko żądania GET, ponieważ POST, PUT i DELETE nie są idempotentne. Błędy klienta (4xx) pomijają ponawianie, ponieważ serwer jawnie odrzucił żądanie.
Gotowy na rozmowy o Angular?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Cachowanie Odpowiedzi z HttpContext
HttpClient udostępnia HttpContext do przekazywania metadanych między interceptorami a kodem wywołującym. Interceptor cachujący może odczytywać tokeny kontekstu, aby określić zachowanie cache.
Token cache i interceptor współpracują ze sobą:
import { HttpInterceptorFn, HttpContextToken, HttpResponse } from '@angular/common/http';
import { of, tap } from 'rxjs';
// Token kontekstu do kontrolowania cachowania per-żądanie
export const CACHE_REQUEST = new HttpContextToken<boolean>(() => false);
const cache = new Map<string, HttpResponse<unknown>>();
export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
// Cachuj tylko jeśli jawnie żądane przez kontekst
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());
}
})
);
};Kod wywołujący włącza cachowanie za pomocą tokena kontekstu:
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),
});
}
}Ten wzorzec utrzymuje zachowanie cachowania jawnym w miejscu wywołania, zamiast ukrywać je w globalnej konfiguracji.
Odświeżanie Tokena z Obsługą 401
Gdy tokeny dostępu wygasają, serwer zwraca odpowiedź 401. Interceptor może przechwycić ten błąd, odświeżyć token i transparentnie ponowić oryginalne żądanie.
Logika odświeżania koordynuje wiele współbieżnych żądań:
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) {
// Czekaj na zakończenie odświeżania
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);
})
);
})
);
};BehaviorSubject zapobiega wielokrotnym wywołaniom odświeżania, gdy kilka żądań kończy się niepowodzeniem jednocześnie. Po odświeżeniu tokena wszystkie żądania w kolejce ponawiają się z nowym tokenem.
Pytania Rekrutacyjne: HttpClient i Interceptory
Rozmowy techniczne często badają zrozumienie wzorców obsługi HTTP. Te pytania pojawiają się często na stanowiskach Angular.
P: Dlaczego funkcjonalne interceptory są preferowane nad interceptorami opartymi na klasach?
Funkcjonalne interceptory mają bardziej przewidywalną kolejność wykonywania. Dokumentacja Angular stwierdza, że funkcjonalne interceptory z withInterceptors() zachowują ścisłą kolejność tablicy, podczas gdy interceptory oparte na DI mogą mieć problemy z kolejnością w złożonych hierarchiach modułów. Funkcjonalne interceptory również produkują mniejsze bundle, ponieważ lepiej się tree-shake'ują.
P: Jak zapobiec uruchomieniu interceptora dla konkretnych żądań?
Istnieją dwa podejścia. Po pierwsze, sprawdzenie wzorca URL w interceptorze i wywołanie next(req) bez modyfikacji dla wykluczonych ścieżek. Po drugie, użycie tokenów HttpContext, które kod wywołujący ustawia, aby zrezygnować z zachowania interceptora. Podejście z kontekstem utrzymuje decyzję o wykluczeniu w miejscu wywołania.
P: Co się dzieje, jeśli zapomnisz wywołać next() w interceptorze?
Żądanie nigdy nie dotrze do serwera. Observable zwrócony przez next() reprezentuje resztę łańcucha interceptorów i rzeczywiste wywołanie HTTP. Bez jego wywołania, downstream interceptory i backend nigdy nie zobaczą żądania. Jest to celowe dla interceptorów cachujących, które zwracają odpowiedzi z cache.
P: Jak obsługiwać współbieżne żądania podczas odświeżania tokena?
Użyj BehaviorSubject do koordynacji. Gdy pierwszy błąd 401 uruchamia odświeżanie, ustaw flagę i wyemituj null na subject. Kolejne błędy 401 czekają na subject z filter, aż nadejdzie nowy token. Zapobiega to wielokrotnym wywołaniom odświeżania i zapewnia, że wszystkie oczekujące żądania ponawiają się z tym samym świeżym tokenem.
P: Czy interceptory mogą modyfikować ciało odpowiedzi?
Tak. Użyj map() na Observable zwróconym przez next(), aby transformować obiekty HttpResponse. Ponieważ odpowiedzi są niemutowalne, klonuj je ze zmodyfikowanymi ciałami. Ten wzorzec działa do dodawania pól obliczonych, rozpakowywania kopert API lub normalizacji struktur danych.
Więcej materiałów do przygotowania do rozmów kwalifikacyjnych z Angular znajdziesz w module pytań rekrutacyjnych HttpClient.
Testowanie Funkcjonalnych Interceptorów
Interceptory wymagają testowania z mockowanymi handlerami HTTP. HttpTestingController Angulara działa z interceptorami zarejestrowanymi przez provideHttpClient().
Konfiguracja testu wygląda następująco:
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' });
});
});Test weryfikuje zarówno wstrzykiwanie nagłówka, jak i wyjątek dla publicznych endpointów bez wykonywania rzeczywistych żądań sieciowych.
Wzorce Produkcyjne: Łączenie Interceptorów
Rzeczywiste aplikacje układają wiele interceptorów w stos. Kolejność ma znaczenie: uwierzytelnianie powinno być przed logowaniem, a cachowanie przed logiką ponawiania.
Konfiguracja produkcyjna może wyglądać tak:
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([
// Kolejność: auth -> refresh -> cache -> retry -> logging
authInterceptor,
tokenRefreshInterceptor,
cacheInterceptor,
retryInterceptor,
loggingInterceptor,
])
),
],
});Ta kolejność zapewnia, że tokeny są dołączane przed obsługą odświeżania, sprawdzenia cache odbywają się przed ponawianiem, a logowanie rejestruje końcowy czas.
Więcej wzorców Angular znajdziesz w module podstaw RxJS, który obejmuje operatory używane w interceptorach.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Kluczowe Wnioski dla Interceptorów Angular HttpClient
- Funkcjonalne interceptory z
withInterceptors()są standardem Angular 20, oferując przewidywalną kolejność i lepszą optymalizację tree-shaking niż alternatywy oparte na klasach - Użyj
inject()wewnątrz interceptorów, aby uzyskać dostęp do serwisów bez szablonowego kodu konstruktora - Klonuj żądania za pomocą
req.clone(), aby modyfikować nagłówki, ponieważ obiektyHttpRequestsą niemutowalne - Tokeny
HttpContextczynią zachowanie per-żądanie jawnym w miejscu wywołania, zamiast ukrywać je w globalnej konfiguracji - Interceptory odświeżania tokenów powinny koordynować współbieżne żądania za pomocą
BehaviorSubject, aby zapobiec wielokrotnym wywołaniom odświeżania - Kolejność interceptorów ma znaczenie: uwierzytelnianie przed cachowaniem, cachowanie przed ponawianiem, logowanie na końcu
Znajdziesz błąd w Angular?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 22 sierpnia 2026
Udostępnij
Powiązane artykuły

Angular Signals i Computed w 2026: Precyzyjna Reaktywność i Pytania Rekrutacyjne
Opanuj Angular Signals, computed, effect i linkedSignal w Angular 20+. Wzorce precyzyjnej reaktywności oraz przygotowanie do rozmów kwalifikacyjnych.

Zaawansowany Dependency Injection w Angular 2026: Providers, Tokens i Pytania Rekrutacyjne
Kompleksowy przewodnik po zaawansowanych mechanizmach Dependency Injection w Angular. Providers, InjectionToken, hierarchia injectorów oraz najczęstsze pytania na rozmowach kwalifikacyjnych.

Składnia Control Flow w Angular 2026: @if, @for, @switch i pytania rekrutacyjne
Kompletny przewodnik po blokowej składni przepływu sterowania w Angular. Poznaj @if, @for, @switch z praktycznymi przykładami kodu i przygotuj się do rozmów kwalifikacyjnych.