HttpClient 具有對應各種 HTTP 動詞的方法,用來發出請求,既可載入資料,也可對伺服器套用變更。每個方法都會回傳一個 RxJS Observable,當它被訂閱時,就會送出請求,並在伺服器回應時發出結果。
NOTE: 由 HttpClient 建立的 Observable 可以被訂閱任意多次,每次訂閱都會向後端發出一個新請求。
透過傳給請求方法的 options 物件,可以調整請求的各種屬性,以及回傳的回應型別。
擷取 JSON 資料
從後端擷取資料通常需要使用 HttpClient.get() 方法發出 GET 請求。此方法接受兩個參數:要擷取資料的字串端點 URL,以及一個用來設定請求的「選用 options」物件。
例如,使用 HttpClient.get() 方法從一個假設的 API 擷取設定資料:
http.get<Config>('/api/config').subscribe((config) => {
// process the configuration.
});
請注意其中的泛型型別參數,它指定伺服器回傳的資料型別為 Config。此參數為選用,如果省略,回傳資料的型別將為 Object。
TIP: 處理結構不確定、且可能含 undefined 或 null 值的資料時,請考慮使用 unknown 型別而非 Object 作為回應型別。
CRITICAL: 請求方法的泛型型別,是對伺服器回傳資料的一種型別「斷言」。HttpClient 不會驗證實際回傳的資料是否符合此型別。
擷取其他型別的資料
HttpClient 預設假設伺服器會回傳 JSON 資料。當與非 JSON 的 API 互動時,你可以在發出請求時告訴 HttpClient 預期何種回應型別。這是透過 responseType 選項完成的。
responseType 值 |
回傳的回應型別 |
|---|---|
'json'(預設) |
給定泛型型別的 JSON 資料 |
'text' |
字串資料 |
'arraybuffer' |
包含原始回應位元組的 ArrayBuffer |
'blob' |
Blob 執行個體 |
例如,你可以要求 HttpClient 把 .jpeg 圖片的原始位元組下載到 ArrayBuffer 中:
http.get('/images/dog.jpg', {responseType: 'arraybuffer'}).subscribe((buffer) => {
console.log('The image is ' + buffer.byteLength + ' bytes large');
});
responseType 的字面值
因為 responseType 的值會影響 HttpClient 回傳的型別,它必須是字面型別,而非 string 型別。
如果傳給請求方法的 options 物件本身是字面物件,這會自動成立;但如果你把請求選項抽出到變數或輔助方法中,你可能需要明確將其指定為字面值,例如 responseType: 'text' as const。
變更伺服器狀態
執行變更的伺服器 API,通常需要發出 POST 請求,並在請求主體中指定新狀態或要套用的變更。
HttpClient.post() 方法的行為與 get() 類似,並在其 options 之前接受一個額外的 body 參數:
http.post<Config>('/api/config', newConfig).subscribe((config) => {
console.log('Updated config:', config);
});
請求的 body 可以提供許多不同型別的值,HttpClient 會相應地將它們序列化:
body 型別 |
序列化為 |
|---|---|
| string | 純文字 |
| number、boolean、array 或 plain object | JSON |
ArrayBuffer |
來自緩衝區的原始資料 |
Blob |
帶有 Blob 內容型別的原始資料 |
FormData |
multipart/form-data 編碼資料 |
HttpParams 或 URLSearchParams |
application/x-www-form-urlencoded 格式字串 |
IMPORTANT: 請記得對變更請求的 Observable 呼叫 .subscribe(),請求才會真正發出。
設定 URL 參數
使用 params 選項,指定應包含在請求 URL 中的請求參數。
傳入物件字面值是設定 URL 參數最簡單的方式:
http
.get('/api/config', {
params: {filter: 'all'},
})
.subscribe((config) => {
// ...
});
或者,如果你需要對參數的建構或序列化有更多控制,可以傳入 HttpParams 的執行個體。
IMPORTANT: HttpParams 的執行個體是「不可變」的,無法直接變更。變更方法(例如 append())會回傳一個套用了變更的新 HttpParams 執行個體。
const baseParams = new HttpParams().set('filter', 'all');
http
.get('/api/config', {
params: baseParams.set('details', 'enabled'),
})
.subscribe((config) => {
// ...
});
你可以使用自訂的 HttpParameterCodec 來建立 HttpParams,由它決定 HttpClient 如何將參數編碼進 URL。
自訂參數編碼
HttpParams 預設使用內建的 HttpUrlEncodingCodec 來編碼與解碼參數的鍵與值。
你可以提供自己的 HttpParameterCodec 實作,自訂編碼與解碼的套用方式。
import {HttpClient, HttpParams, HttpParameterCodec} from '@angular/common/http';
import {inject} from '@angular/core';
export class CustomHttpParamEncoder implements HttpParameterCodec {
encodeKey(key: string): string {
return encodeURIComponent(key);
}
encodeValue(value: string): string {
return encodeURIComponent(value);
}
decodeKey(key: string): string {
return decodeURIComponent(key);
}
decodeValue(value: string): string {
return decodeURIComponent(value);
}
}
export class ApiService {
private http = inject(HttpClient);
search() {
const params = new HttpParams({
encoder: new CustomHttpParamEncoder(),
})
.set('email', '[email protected]')
.set('q', 'a & b? c/d = e');
return this.http.get('/api/items', {params});
}
}
設定請求標頭
使用 headers 選項,指定應包含在請求中的請求標頭。
傳入物件字面值是設定請求標頭最簡單的方式:
http
.get('/api/config', {
headers: {
'X-Debug-Level': 'verbose',
},
})
.subscribe((config) => {
// ...
});
或者,如果你需要對標頭的建構有更多控制,可以傳入 HttpHeaders 的執行個體。
IMPORTANT: HttpHeaders 的執行個體是「不可變」的,無法直接變更。變更方法(例如 append())會回傳一個套用了變更的新 HttpHeaders 執行個體。
const baseHeaders = new HttpHeaders().set('X-Debug-Level', 'minimal');
http
.get<Config>('/api/config', {
headers: baseHeaders.set('X-Debug-Level', 'verbose'),
})
.subscribe((config) => {
// ...
});
與伺服器回應事件互動
為了方便,HttpClient 預設回傳伺服器回傳資料(回應主體)的 Observable。有時我們會想檢視實際的回應,例如擷取特定的回應標頭。
若要存取完整的回應,請將 observe 選項設定為 'response':
http.get<Config>('/api/config', {observe: 'response'}).subscribe((res) => {
console.log('Response status:', res.status);
console.log('Body:', res.body);
});
observe 的字面值
因為 observe 的值會影響 HttpClient 回傳的型別,它必須是字面型別,而非 string 型別。
如果傳給請求方法的 options 物件本身是字面物件,這會自動成立;但如果你把請求選項抽出到變數或輔助方法中,你可能需要明確將其指定為字面值,例如 observe: 'response' as const。
接收原始進度事件
除了回應主體或回應物件之外,HttpClient 也可以回傳一串原始「事件」,對應請求生命週期中的特定時刻。這些事件包括請求送出時、回應標頭回傳時,以及主體完成時。這些事件還可以包括「進度事件」,回報大型請求或回應主體的上傳與下載狀態。
進度事件預設為停用(因為它們有效能成本),但可以透過 reportUploadProgress 與 reportDownloadProgress 選項啟用。
NOTE: HttpClient 的預設 fetch 後端不支援「上傳」進度事件,如果你設定 reportUploadProgress 將會擲出錯誤。如果你的應用程式需要上傳進度事件,請在 provideHttpClient(...) 中以 withXhr() 設定 HttpClient。
若要觀察事件串流,請將 observe 選項設定為 'events':
http
.get('/api/download', {
reportDownloadProgress: true,
observe: 'events',
})
.subscribe((event) => {
switch (event.type) {
case HttpEventType.DownloadProgress:
console.log('Downloaded ' + event.loaded + ' out of ' + event.total + ' bytes');
break;
case HttpEventType.Response:
console.log('Finished downloading!');
break;
}
});
observe 的字面值
因為 observe 的值會影響 HttpClient 回傳的型別,它必須是字面型別,而非 string 型別。
如果傳給請求方法的 options 物件本身是字面物件,這會自動成立;但如果你把請求選項抽出到變數或輔助方法中,你可能需要明確將其指定為字面值,例如 observe: 'events' as const。
事件串流中回報的每個 HttpEvent 都有一個 type,用來區分該事件代表什麼:
type 值 |
事件意義 |
|---|---|
HttpEventType.Sent |
請求已送往伺服器 |
HttpEventType.UploadProgress |
一個 HttpUploadProgressEvent,回報請求主體的上傳進度 |
HttpEventType.ResponseHeader |
已收到回應的開頭,包括狀態與標頭 |
HttpEventType.DownloadProgress |
一個 HttpDownloadProgressEvent,回報回應主體的下載進度 |
HttpEventType.Response |
已收到完整回應,包括回應主體 |
HttpEventType.User |
來自 HTTP 攔截器的自訂事件。 |
處理請求失敗
HTTP 請求可能以三種方式失敗:
- 網路或連線錯誤可能使請求無法到達後端伺服器。
- 當設定了 timeout 選項時,請求未在時間內回應。
- 後端可以收到請求,但處理失敗,並回傳錯誤回應。
HttpClient 會將上述所有類型的錯誤擷取在 HttpErrorResponse 中,並透過 Observable 的錯誤通道回傳。網路與逾時錯誤的 status 碼為 0。對於逾時,error 是名為 TimeoutError 的 DOMException;對於網路錯誤,則是 fetch 擲出的錯誤(或使用 withXhr() 時為 ProgressEvent)。後端錯誤帶有後端回傳的失敗 status 碼,並以錯誤回應作為 error。請檢查回應,以找出錯誤的成因,以及處理錯誤的適當行動。
RxJS 函式庫提供多個對錯誤處理有幫助的運算子。
你可以使用 catchError 運算子,將錯誤回應轉換為給 UI 使用的值。這個值可以告訴 UI 顯示錯誤頁面或錯誤值,並在必要時擷取錯誤的成因。
有時,網路中斷等暫時性錯誤可能導致請求意外失敗,而只要重試請求就能成功。RxJS 提供多個「重試」運算子,會在特定條件下自動重新訂閱失敗的 Observable。例如,retry() 運算子會自動嘗試重新訂閱指定的次數。
逾時
若要為請求設定逾時,你可以將 timeout 選項連同其他請求選項一起設定為毫秒數。如果後端請求未在指定時間內完成,請求將被中止,並發出一個錯誤。
NOTE: 逾時只會套用到後端 HTTP 請求本身。它不是整個請求處理鏈的逾時。因此,此選項不受攔截器引入的任何延遲影響。
http
.get('/api/config', {
timeout: 3000,
})
.subscribe({
next: (config) => {
console.log('Config fetched successfully:', config);
},
error: (err) => {
// If the request times out, an error will have been emitted.
},
});
進階 fetch 選項
Angular 的 HttpClient 支援進階的 fetch API 選項,可以提升效能與使用者體驗。這些選項在使用 fetch 後端時可用,而 fetch 後端是預設後端。
Fetch 選項
下列選項在使用 fetch 後端時,能對請求行為提供細粒度的控制。
保持連線(keep-alive)
keepalive 選項讓請求的生命週期能超過發起它的頁面。這對於分析或記錄請求特別有用,因為這類請求即使在使用者離開頁面後也需要完成。
http
.post('/api/analytics', analyticsData, {
keepalive: true,
})
.subscribe();
HTTP 快取控制
cache 選項控制請求如何與瀏覽器的 HTTP 快取互動,這能顯著提升重複請求的效能。
// Use cached response regardless of freshness
http
.get('/api/config', {
cache: 'force-cache',
})
.subscribe((config) => {
// ...
});
// Always fetch from network, bypass cache
http
.get('/api/live-data', {
cache: 'no-cache',
})
.subscribe((data) => {
// ...
});
// Use cached response only, fail if not in cache
http
.get('/api/static-data', {
cache: 'only-if-cached',
})
.subscribe((data) => {
// ...
});
用於 Core Web Vitals 的請求優先級
priority 選項讓你指出請求的相對重要性,協助瀏覽器最佳化資源載入,以獲得更好的 Core Web Vitals 分數。
// High priority for critical resources
http
.get('/api/user-profile', {
priority: 'high',
})
.subscribe((profile) => {
// ...
});
// Low priority for non-critical resources
http
.get('/api/recommendations', {
priority: 'low',
})
.subscribe((recommendations) => {
// ...
});
// Auto priority (default) lets the browser decide
http
.get('/api/settings', {
priority: 'auto',
})
.subscribe((settings) => {
// ...
});
可用的 priority 值:
'high':高優先級,提早載入(例如關鍵使用者資料、首屏即可見的內容)'low':低優先級,在資源可用時載入(例如分析、預擷取資料)'auto':瀏覽器依請求上下文決定優先級(預設)
TIP: 對於影響最大內容繪製(LCP)的請求,請使用 priority: 'high';對於不影響初始使用者體驗的請求,請使用 priority: 'low'。
請求模式
mode 選項控制請求如何處理跨來源請求,並決定回應型別。
// Same-origin requests only
http
.get('/api/local-data', {
mode: 'same-origin',
})
.subscribe((data) => {
// ...
});
// CORS-enabled cross-origin requests
http
.get('https://api.external.com/data', {
mode: 'cors',
})
.subscribe((data) => {
// ...
});
// No-CORS mode for simple cross-origin requests
http
.get('https://external-api.com/public-data', {
mode: 'no-cors',
})
.subscribe((data) => {
// ...
});
可用的 mode 值:
'same-origin':只允許同來源請求,跨來源請求將失敗'cors':允許搭配 CORS 的跨來源請求(預設)'no-cors':允許不搭配 CORS 的簡單跨來源請求,回應為不透明(opaque)
TIP: 在瀏覽器中,對於絕不應跨來源的敏感請求,請使用 mode: 'same-origin'。
IMPORTANT: 在 Node.js 上進行 SSR 期間,HttpClient 使用 Node.js 的 Undici 版 Fetch 實作。Undici 不強制執行瀏覽器的 CORS 檢查,因此 mode: 'same-origin' 不會限制伺服器端請求。請針對允許清單驗證受使用者影響的 URL。
重新導向處理
redirect 選項指定如何處理來自伺服器的重新導向回應。
// Follow redirects automatically (default behavior)
http
.get('/api/resource', {
redirect: 'follow',
})
.subscribe((data) => {
// ...
});
// Prevent automatic redirects
http
.get('/api/resource', {
redirect: 'manual',
})
.subscribe((response) => {
// Handle redirect manually
});
// Treat redirects as errors
http
.get('/api/resource', {
redirect: 'error',
})
.subscribe({
next: (data) => {
// Success response
},
error: (err) => {
// Redirect responses will trigger this error handler
},
});
可用的 redirect 值:
'follow':自動跟隨重新導向(預設)'error':將重新導向視為錯誤'manual':不自動跟隨重新導向,回傳重新導向回應
TIP: 當你需要以自訂邏輯處理重新導向時,請使用 redirect: 'manual'。
憑證處理
credentials 選項控制在跨來源請求時,是否隨請求傳送 cookie、授權標頭及其他憑證。這在驗證情境中特別重要。
// Include credentials for cross-origin requests
http
.get('https://api.example.com/protected-data', {
credentials: 'include',
})
.subscribe((data) => {
// ...
});
// Never send credentials (default for cross-origin)
http
.get('https://api.example.com/public-data', {
credentials: 'omit',
})
.subscribe((data) => {
// ...
});
// Send credentials only for same-origin requests
http
.get('/api/user-data', {
credentials: 'same-origin',
})
.subscribe((data) => {
// ...
});
// withCredentials overrides credentials setting
http
.get('https://api.example.com/data', {
credentials: 'omit', // This will be ignored
withCredentials: true, // This forces credentials: 'include'
})
.subscribe((data) => {
// Request will include credentials despite credentials: 'omit'
});
// Legacy approach (still supported)
http
.get('https://api.example.com/data', {
withCredentials: true,
})
.subscribe((data) => {
// Equivalent to credentials: 'include'
});
IMPORTANT: withCredentials 選項的優先級高於 credentials 選項。如果兩者都指定,withCredentials: true 將一律導致 credentials: 'include',無論明確指定的 credentials 值為何。
可用的 credentials 值:
'omit':永不傳送憑證'same-origin':只對同來源請求傳送憑證(預設)'include':一律傳送憑證,即使是跨來源請求
TIP: 當你需要把驗證 cookie 或標頭傳送到支援 CORS 的不同網域時,請使用 credentials: 'include'。避免混用 credentials 與 withCredentials 選項,以免混淆。
IMPORTANT: 在 Node.js 上進行 SSR 期間,credentials: 'include' 不會自動轉送來自瀏覽器請求的 cookie。credentials 選項不會移除你明確加入的 Cookie 或 Authorization 標頭。Undici 允許某些瀏覽器禁止的標頭,因此只應將憑證標頭轉送至受信任的來源。
Referrer
referrer 選項讓你控制隨請求傳送的 referrer 資訊。這對隱私與安全考量很重要。
// Send a specific referrer URL
http
.get('/api/data', {
referrer: 'https://example.com/page',
})
.subscribe((data) => {
// ...
});
// Use the current page as referrer (default behavior)
http
.get('/api/analytics', {
referrer: 'about:client',
})
.subscribe((data) => {
// ...
});
referrer 選項接受:
- 有效的 URL 字串:設定要傳送的特定 referrer URL
- 空字串
'':不傳送 referrer 資訊 'about:client':使用預設 referrer(目前頁面的 URL)
TIP: 對於不希望洩漏來源頁面 URL 的敏感請求,請使用 referrer: ''。
Referrer 政策
referrerPolicy 選項控制隨 HTTP 請求傳送多少 referrer 資訊,也就是發出請求頁面的 URL。此設定同時影響隱私與分析,讓你在資料可見性與安全考量之間取得平衡。
// Send no referrer information regardless of the current page
http
.get('/api/data', {
referrerPolicy: 'no-referrer',
})
.subscribe();
// Send origin only (e.g. https://example.com)
http
.get('/api/analytics', {
referrerPolicy: 'origin',
})
.subscribe();
referrerPolicy 選項接受:
'no-referrer'永不傳送Referer標頭。'no-referrer-when-downgrade'對同來源與安全(HTTPS→HTTPS)請求傳送 referrer,但從安全來源導向較不安全的來源(HTTPS→HTTP)時省略。'origin'只傳送 referrer 的來源(scheme、host、port),省略路徑與查詢資訊。'origin-when-cross-origin'對同來源請求傳送完整 URL,但對跨來源請求只傳送來源。'same-origin'對同來源請求傳送完整 URL,對跨來源請求不傳送 referrer。'strict-origin'只傳送來源,且僅在通訊協定安全等級未降級時(例如 HTTPS→HTTPS)。降級時省略 referrer。'strict-origin-when-cross-origin'瀏覽器的預設行為。對同來源請求傳送完整 URL;對未降級的跨來源請求傳送來源;降級時省略 referrer。'unsafe-url'一律傳送完整 URL(包括路徑與查詢)。這可能暴露敏感資料,應謹慎使用。
TIP: 對於注重隱私的請求,請優先使用保守的值,例如 'no-referrer'、'origin' 或 'strict-origin-when-cross-origin'。
完整性
integrity 選項讓你透過提供預期內容的加密雜湊,驗證回應是否遭到竄改。這在從 CDN 載入指令碼或其他資源時特別有用。
// Verify response integrity with SHA-256 hash
http
.get('/api/script.js', {
integrity: 'sha256-ABC123...',
responseType: 'text',
})
.subscribe((script) => {
// Script content is verified against the hash
});
IMPORTANT: integrity 選項要求回應內容與所提供的雜湊完全相符。如果內容不符,請求將以網路錯誤失敗。
CRITICAL: 在 SSR 期間,Fetch 實作會在回傳回應之前讀取整個回應主體來驗證 integrity,這是 Fetch 標準的要求。Angular 只有在 Fetch 回傳回應之後,才強制執行 maxResponseBodySize,因此此限制不會約束在完整性驗證期間緩衝的資料。
TIP: 從外部來源載入關鍵資源時,請使用子資源完整性(subresource integrity)來確保它們未被修改。請使用 openssl 等工具產生雜湊。
HTTP Observable
HttpClient 上的每個請求方法都會建構並回傳一個所請求回應型別的 Observable。在使用 HttpClient 時,了解這些 Observable 如何運作非常重要。
HttpClient 產生 RxJS 所謂的「冷」Observable,意思是除非 Observable 被訂閱,否則不會發生真正的請求。只有到那時,請求才會真正送往伺服器。多次訂閱同一個 Observable 將觸發多次後端請求。每次訂閱都是獨立的。
TIP: 你可以把 HttpClient 的 Observable 想像成真正伺服器請求的「藍圖」。
一旦訂閱,取消訂閱將中止進行中的請求。如果 Observable 是透過 async 管道訂閱的,這非常有用,因為當使用者離開目前頁面時,它會自動取消請求。此外,如果你搭配 RxJS 組合器(例如 switchMap)使用 Observable,這種取消機制會清除所有過時的請求。
回應回傳後,來自 HttpClient 的 Observable 通常會完成(但攔截器可能影響這一點)。
由於會自動完成,如果不清理 HttpClient 的訂閱,通常沒有記憶體洩漏的風險。然而,如同任何非同步操作,我們強烈建議在元件銷毀時清理訂閱,否則訂閱回呼可能會執行,並在試圖與已銷毀的元件互動時遇到錯誤。
TIP: 使用 async 管道或 toSignal 操作來訂閱 Observable,可確保訂閱被妥善處置。
最佳實務
雖然 HttpClient 可以直接注入並在元件中使用,我們一般建議你建立可重複使用、可注入的服務,將資料存取邏輯隔離並封裝。例如,這個 UserService 封裝了依 id 請求使用者資料的邏輯:
@Service()
export class UserService {
private http = inject(HttpClient);
getUser(id: string): Observable<User> {
return this.http.get<User>(`/api/user/${id}`);
}
}
在元件內,你可以結合 @if 與 async 管道,僅在資料載入完成後,才渲染該資料的 UI:
import {AsyncPipe} from '@angular/common';
import {Component, input, inject, effect} from '@angular/core';
import {Observable} from 'rxjs';
@Component({
imports: [AsyncPipe],
template: `
@if (user$ | async; as user) {
<p>Name: {{ user.name }}</p>
<p>Biography: {{ user.biography }}</p>
}
`,
})
export class UserProfile {
userId = input.required<string>();
user$!: Observable<User>;
private userService = inject(UserService);
constructor() {
effect(() => {
this.user$ = this.userService.getUser(this.userId());
});
}
}