Angular HttpClient 请求指南:从 GET/POST 到高级 Fetch 选项的完整实战解析
HttpClient 是 Angular 官方 @angular/common/http 包提供的 HTTP 客户端服务,本文基于当前仓库中 making-requests.md 指南文档,系统讲解用 HttpClient 发起请求、读取不同类型响应、变更服务器状态、设置 URL 参数与请求头、监听进度事件、处理失败以及使用高级 Fetch 选项的完整方法。读完本文,你将掌握 HttpClient 全部请求选项的语义与取值、Observable 的底层行为特性,以及如何结合服务封装写出健壮可维护的数据访问层。文章还穿插引用仓库中 packages/common/http 下的真实源码作为实现证据。
请求方法与返回的 Observable
HttpClient 提供了与不同 HTTP 动词一一对应的方法,既用于加载数据,也用于在服务器上执行变更(mutation)。每个方法都会返回一个 RxJS Observable。整个 HTTP 客户端的主体实现位于 client.ts 的 HttpClient 类中,每个公开方法(get、post、put、delete、patch、head、options 等)最终都汇聚到统一的 request() 方法构造 HttpRequest。
两个需要牢记的核心行为:
- 订阅才发请求:这些
Observable是 RxJS 的 "cold" Observable,只有被.subscribe()之后,请求才会真正发送到服务器。 - 订阅 N 次 = 发送 N 次后端请求:由
HttpClient创建的Observable可以被任意次订阅,且每次订阅都会触发一次全新的后端请求,彼此相互独立。
请求方法的最后一个可选参数是一个 options 对象,通过它可以调整请求的各个方面以及返回的响应类型。从源码看,这些选项统一收敛到 request.ts 中定义的 HttpRequestOptions 接口,包括 headers、params、responseType、observe(属于 HttpClientCommonOptions)、reportProgress、timeout、withCredentials、credentials、keepalive、cache、priority、mode、redirect、referrer、referrerPolicy、integrity、transferCache、context 等,本文后续章节会逐一说明其中常用项。
在开始使用前若尚未配置 HTTP 客户端,请先参考 setup.md(通常是在应用配置中使用
provideHttpClient()注册)。
获取 JSON 数据
从后端加载数据最常见的做法是用 HttpClient.get() 发起 GET 请求。它接收两个参数:端点 URL 字符串,以及一个可选的 options 配置对象。
http.get<Config>('/api/config').subscribe((config) => {
// process the configuration.
});
注意代码中的泛型类型参数:它声明服务器返回的数据会被当作 Config 类型使用。这个泛型参数是可选的,省略时返回数据的类型为 Object。
- TIP:当数据的结构不确定、且可能包含
undefined或null时,建议用unknown类型而不是Object作为响应类型,迫使你在使用前先做类型收窄。 - CRITICAL(重要):请求方法的泛型只是对服务器返回数据的类型断言,
HttpClient并不会校验真实返回的数据是否真的匹配该类型。泛型写错不会在运行时暴露,解析逻辑仍由responseType决定。
获取其他类型的数据
默认情况下 HttpClient 假设服务器返回 JSON。当对接非 JSON 接口时,可以通过 responseType 选项告知 HttpClient 期望的响应类型:
responseType 值 |
返回的响应类型 |
|---|---|
'json'(默认) |
指定泛型类型的 JSON 数据 |
'text' |
字符串数据 |
'arraybuffer' |
包含原始响应字节的 ArrayBuffer |
'blob' |
Blob 实例 |
例如把一张 .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 是内联字面量对象时这会自动成立;但如果你把 options 提取到变量或辅助方法中,可能需要显式声明为字面量,例如responseType: 'text' as const。
从源码看,响应解析集中在 fetch.ts 的 parseBody():'json' 会把响应体解码为文本后 JSON.parse(并剥离可能的 XSSI 前缀 )]}',\n),'text' 依据 Content-Type 中的 charset 解码,'blob' 包装成带 content type 的 Blob,'arraybuffer' 直接返回原始字节缓冲区。因此,即便服务器返回非 JSON 内容,只要 responseType 设置正确,也能被正确解析。
变更服务器状态
执行变更操作的接口通常要求用 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 或普通对象 | JSON |
ArrayBuffer |
缓冲区的原始数据 |
Blob |
原始数据,带 Blob 自身的 content type |
FormData |
multipart/form-data 编码数据 |
HttpParams 或 URLSearchParams |
application/x-www-form-urlencoded 格式字符串 |
上述规则可以直接在 request.ts 的 serializeBody() 与 detectContentTypeHeader() 中找到对应实现:字符串、ArrayBuffer、Blob、FormData、URLSearchParams 原样透传;HttpParams 实例调用其 toString();对象、布尔值、数组走 JSON.stringify。同时,若未显式设置 Content-Type,detectContentTypeHeader() 会自动推断:字符串为 text/plain、HttpParams 为 application/x-www-form-urlencoded;charset=UTF-8、数组/对象/数值/布尔为 application/json,Blob 使用其自身的 type,而 FormData 与 ArrayBuffer 交由底层自动处理(fetch.ts 在构造 fetch 参数时调用该方法)。
IMPORTANT:请务必记得对变更类请求的
Observable执行.subscribe(),否则请求根本不会真正发出。
设置 URL 参数
使用 params 选项指定应包含在请求 URL 中的查询参数。
最简单的做法是传入一个对象字面量:
http
.get('/api/config', {
params: {filter: 'all'},
})
.subscribe((config) => {
// ...
});
如果需要对参数的构造或序列化做更精细的控制,可以传入一个 HttpParams 实例。
IMPORTANT:
HttpParams实例是**不可变(immutable)**的,无法直接修改。诸如append()之类的变更方法会返回一个应用了变更的新实例。
const baseParams = new HttpParams().set('filter', 'all');
http
.get('/api/config', {
params: baseParams.set('details', 'enabled'),
})
.subscribe((config) => {
// ...
});
HttpParams 在构造时支持 HttpParamsOptions 中的几个可选配置:fromString(直接以查询字符串格式解析)、fromObject(以对象映射构建)、encoder(自定义编解码器)。若同时传入 fromString 与 fromObject,构造器会直接抛出运行时错误(见 params.ts)。
另一个值得留意的细节是 URL 拼接逻辑:HttpRequest 构造函数在把序列化后的参数拼接到 URL 时会先剥离 # 之后的 fragment,把查询串插入到 fragment 之前,避免参数被追加到 fragment 后面、只在浏览器端可见从而绕过服务端安全校验的情况。
HttpParams 还可以搭配自定义 HttpParameterCodec,决定 HttpClient 如何把参数编码进 URL。
自定义参数编码
默认情况下,HttpParams 使用内置的 HttpUrlEncodingCodec 来编码/解码参数键与值。这个默认 codec 的实现逻辑位于 params.ts:先对键值执行 encodeURIComponent,随后再把 @、:、$、,、;、=、?、/ 这八个字符还原为字面量(这些字符在 URL 查询串中通常可以安全保留)。
你也可以提供自己的 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', 'dev+alerts@example.com')
.set('q', 'a & b? c/d = e');
return this.http.get('/api/items', {params});
}
}
HttpParameterCodec 接口本身定义于 params.ts,要求实现 encodeKey、encodeValue、decodeKey、decodeValue 四个方法。
设置请求头
使用 headers 选项指定请求中应包含的请求头。同样可以传对象字面量:
http
.get('/api/config', {
headers: {
'X-Debug-Level': 'verbose',
},
})
.subscribe((config) => {
// ...
});
需要更精细地构造请求头时,可以传入 HttpHeaders 实例。
IMPORTANT:与
HttpParams一样,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) => {
// ...
});
从 headers.ts 源码可以看到,HttpHeaders 底层对 header 名称做了规范化与小写化存储(has/get 时大小写不敏感),并提供了 has、get、getAll、append、set、delete 等方法,其中 append/set/delete 都返回新实例以维持不可变性。
与服务器响应对象交互
为方便起见,HttpClient 默认返回的 Observable 直接产出服务器返回的数据(即响应体)。但有时我们需要查看完整的响应,例如读取某些特定的响应头。
此时把 observe 选项设为 'response':
http.get<Config>('/api/config', {observe: 'response'}).subscribe((res) => {
console.log('Response status:', res.status);
console.log('Body:', res.body);
});
从 HttpClient 的方法重载可以看出(client.ts),observe 有三个合法取值:'body'(默认,返回响应体)、'response'(返回完整 HttpResponse)、'events'(返回事件流)。HttpResponse 与 HttpHeaderResponse、HttpErrorResponse 共同继承自 HttpResponseBase(定义见 response.ts),包含 status、statusText、headers、url 等字段。
observe必须是字面量类型:由于observe的值同样决定HttpClient的返回类型,它必须是字面量而不能是宽泛的string类型。options 内联在方法调用处时自动成立;若提取到变量或辅助方法中,需要显式写成observe: 'response' as const。
接收原始进度事件
除了响应体或响应对象,HttpClient 还能返回一条对应请求生命周期特定时刻的原始事件流。这些事件包括:请求被发出、收到响应头、响应体接收完毕等时刻;当请求或响应体较大时,还可能包含报告上传/下载状态的进度事件(progress events)。
进度事件默认是关闭的(因为会产生性能开销),可通过 reportProgress 选项开启。若同时需要区分上传/下载,仓库较新版本还引入了更细粒度的 reportUploadProgress 与 reportDownloadProgress 选项(见 request.ts)。
NOTE:
HttpClient默认的 fetch 后端不报告上传进度事件。如果你的应用需要上传进度事件,请在provideHttpClient(...)配置中使用withXhr()(XHR 后端)。对应的运行时限制可在 fetch.ts 中看到:当请求设置了reportUploadProgress时,FetchBackend会直接抛出提示改用withXhr()的运行时错误。withFetch/withXhr两个特性的定义位于 provider.ts。
要观察事件流,把 observe 设为 'events':
http
.post('/api/upload', myData, {
reportProgress: true,
observe: 'events',
})
.subscribe((event) => {
switch (event.type) {
case HttpEventType.UploadProgress:
console.log('Uploaded ' + event.loaded + ' out of ' + event.total + ' bytes');
break;
case HttpEventType.Response:
console.log('Finished uploading!');
break;
}
});
observe必须是字面量类型:同样地,若把 options 提取到变量中,请使用observe: 'events' as const。
事件流中报告的每个 HttpEvent 都带有一个 type 字段,用于区分事件含义(HttpEventType 枚举定义于 response.ts):
type 值 |
事件含义 |
|---|---|
HttpEventType.Sent |
请求已派发给服务器 |
HttpEventType.UploadProgress |
HttpUploadProgressEvent,报告上传请求体的进度 |
HttpEventType.ResponseHeader |
已收到响应头部,包含状态码与响应头 |
HttpEventType.DownloadProgress |
HttpDownloadProgressEvent,报告下载响应体的进度 |
HttpEventType.Response |
已收到完整响应,包括响应体 |
HttpEventType.User |
来自 HTTP 拦截器(interceptor)的自定义事件 |
其中进度事件类型 HttpProgressEvent、HttpDownloadProgressEvent、HttpUploadProgressEvent 的定义同样位于 response.ts。关于利用 HttpEventType.User 与拦截器协作的更多内容,可参考 interceptors.md。
处理请求失败
HTTP 请求可能以三种方式失败:
- 网络或连接错误:请求无法到达后端服务器;
- 请求超时:设置了
timeout选项但请求未在限定时间内完成; - 后端错误响应:服务器收到了请求但处理失败,返回了错误状态码。
HttpClient 会把上述所有错误统一封装成 HttpErrorResponse,并通过 Observable 的错误通道(error channel)抛给订阅者。其中:
- 网络错误与超时错误的
status为0,error是一个ProgressEvent实例; - 后端错误则带有后端返回的失败
status码,error为错误响应体。
可以检查该响应来判断错误成因并决定相应的处理动作。对应底层逻辑见 fetch.ts:status 在 2xx 区间内走成功通道发出 HttpResponse 并 complete(),否则通过 observer.error(new HttpErrorResponse(...)) 走错误通道;当用户请求 JSON 但响应体解析失败时,成功状态也可能转为错误。
RxJS 库提供了多个对错误处理有用的操作符:
catchError:把错误响应转换为可供 UI 展示的值,例如让 UI 显示错误页或占位值,必要时捕获错误原因。- retry 系列操作符:像网络中断这类瞬时错误,直接重试请求往往就能成功。例如
retry()操作符会自动重订阅失败的Observable指定的次数。
请求超时
可以为请求设置 timeout 选项(单位毫秒),与其它请求选项一并传入。若后端请求在限定时间内未完成,请求将被中止并抛出一个错误:
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.
},
});
NOTE:
timeout只作用于后端 HTTP 请求本身,而不是整条请求处理链路的超时。因此拦截器引入的任何延迟都不会影响该选项。
实现层面有两个细节值得了解(request.ts):timeout 必须是正整数,小于 1 或非整数会在构造请求时抛出 RuntimeError;超时计时通过 AbortController 实现,且运行在 Angular Zone 之外以避免多余的变更检测(fetch.ts)。另外,HttpClient 订阅被取消时也会调用 aborter.abort() 中止进行中的请求(fetch.ts)。
高级 Fetch 选项
当使用 fetch 后端(默认后端)时,HttpClient 支持一系列对标浏览器 Fetch API 的高级选项,可用于提升性能与用户体验。这些选项最终在 fetch.ts 的 createRequestInit() 中逐一映射到底层 fetch() 的 RequestInit。下文逐一说明其语义与适用场景。
Keep-alive 长连接
keepalive 选项允许请求的生命周期超出发起它的页面——即使页面已被卸载,请求仍可继续完成。这对于埋点统计或日志上报类请求尤其有用,它们需要在用户导航离开页面后仍然跑完:
http
.post('/api/analytics', analyticsData, {
keepalive: true,
})
.subscribe();
HTTP 缓存控制
cache 选项控制请求与浏览器 HTTP 缓存的交互方式,对重复请求的性能有显著影响:
// 直接使用缓存响应,无论是否新鲜
http
.get('/api/config', {
cache: 'force-cache',
})
.subscribe((config) => {
// ...
});
// 总是从网络获取,绕过缓存
http
.get('/api/live-data', {
cache: 'no-cache',
})
.subscribe((data) => {
// ...
});
// 只使用缓存响应,缓存中不存在则失败
http
.get('/api/static-data', {
cache: 'only-if-cached',
})
.subscribe((data) => {
// ...
});
面向 Core Web Vitals 的请求优先级
priority 选项用于声明请求的相对重要程度,帮助浏览器优化资源加载顺序,从而改善 Core Web Vitals 指标:
// 关键资源使用高优先级
http
.get('/api/user-profile', {
priority: 'high',
})
.subscribe((profile) => {
// ...
});
// 非关键资源使用低优先级
http
.get('/api/recommendations', {
priority: 'low',
})
.subscribe((recommendations) => {
// ...
});
// auto(默认)交给浏览器决定
http
.get('/api/settings', {
priority: 'auto',
})
.subscribe((settings) => {
// ...
});
可用的 priority 取值:
'high':高优先级,尽早加载(例如关键用户数据、首屏以上内容);'low':低优先级,资源可用时再加载(例如埋点、预取数据);'auto':由浏览器根据请求上下文决定优先级(默认值)。
TIP:对影响 Largest Contentful Paint(LCP)的请求使用
priority: 'high',对不影响首屏体验的请求使用priority: 'low'。
请求模式(跨域策略)
mode 选项控制请求如何处理跨域,并决定响应类型:
// 仅允许同源请求
http
.get('/api/local-data', {
mode: 'same-origin',
})
.subscribe((data) => {
// ...
});
// 允许启用 CORS 的跨域请求
http
.get('https://api.external.com/data', {
mode: 'cors',
})
.subscribe((data) => {
// ...
});
// 无 CORS 的简单跨域请求
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(SSR 注意事项):在 Node.js 上执行 SSR 时,
HttpClient使用的是基于 Undici 的 Node.js fetch 实现,而 Undici 不会执行浏览器式的 CORS 检查,因此mode: 'same-origin'并不能限制服务端请求。若有用户可影响的 URL,务必通过白名单(allowlist)校验。
重定向处理
redirect 选项指定如何处理服务器返回的重定向响应:
// 自动跟随重定向(默认行为)
http
.get('/api/resource', {
redirect: 'follow',
})
.subscribe((data) => {
// ...
});
// 阻止自动重定向
http
.get('/api/resource', {
redirect: 'manual',
})
.subscribe((response) => {
// 手动处理重定向
});
// 把重定向视为错误
http
.get('/api/resource', {
redirect: 'error',
})
.subscribe({
next: (data) => {
// 成功响应
},
error: (err) => {
// 重定向响应会触发此错误处理
},
});
可用的 redirect 取值:
'follow':自动跟随重定向(默认);'error':把重定向视为错误;'manual':不自动跟随重定向,返回重定向响应。
TIP:需要自定义重定向逻辑时使用
redirect: 'manual'。
凭据(Credentials)处理
credentials 选项控制跨域请求是否携带 cookie、授权头等凭据,这对认证场景尤为重要:
// 跨域请求携带凭据
http
.get('https://api.example.com/protected-data', {
credentials: 'include',
})
.subscribe((data) => {
// ...
});
// 绝不发送凭据(跨域的默认值)
http
.get('https://api.example.com/public-data', {
credentials: 'omit',
})
.subscribe((data) => {
// ...
});
// 仅同源请求发送凭据
http
.get('/api/user-data', {
credentials: 'same-origin',
})
.subscribe((data) => {
// ...
});
// withCredentials 会覆盖 credentials 设置
http
.get('https://api.example.com/data', {
credentials: 'omit', // 这一行将被忽略
withCredentials: true, // 这会强制 credentials: 'include'
})
.subscribe((data) => {
// 请求仍会携带凭据
});
// 传统写法(仍受支持)
http
.get('https://api.example.com/data', {
withCredentials: true,
})
.subscribe((data) => {
// 等价于 credentials: 'include'
});
IMPORTANT:
withCredentials的优先级高于credentials。当两者同时指定时,withCredentials: true始终产生credentials: 'include'的效果,无论显式传入的credentials值是什么。源码 fetch.ts 在构造请求参数时正是这样处理:先取req.credentials,随后若withCredentials为真则强制改写为'include'并输出一条开发警告。为了避免混淆,请勿混用这两个选项。
可用的 credentials 取值:
'omit':绝不发送凭据;'same-origin':仅同源请求发送凭据(默认);'include':总是发送凭据,包括跨域请求。
TIP:当需要向支持 CORS 的不同域发送认证 cookie 或请求头时使用
credentials: 'include'。
IMPORTANT(SSR 注意事项):SSR 期间
credentials: 'include'不会自动转发来自浏览器请求的 cookie。credentials选项也不会移除你显式添加的Cookie或Authorization请求头;而 Undici 允许发送一些浏览器禁止的请求头。因此只应把凭据类请求头转发给可信来源(trusted origins)。
Referrer
referrer 选项用于控制请求携带的 referrer 信息,涉及隐私与安全考量:
// 发送特定的 referrer URL
http
.get('/api/data', {
referrer: 'https://example.com/page',
})
.subscribe((data) => {
// ...
});
// 使用当前页面作为 referrer(默认行为)
http
.get('/api/analytics', {
referrer: 'about:client',
})
.subscribe((data) => {
// ...
});
referrer 选项可接受的值:
- 合法的 URL 字符串:设置要发送的具体 referrer URL;
- 空字符串
'':不发送任何 referrer 信息; 'about:client':使用默认 referrer(当前页面 URL)。
TIP:对于不想泄露来源页面 URL 的敏感请求,使用
referrer: ''。
Referrer Policy
referrerPolicy 选项控制随请求发送多少 referrer 信息(即发起请求的页面 URL),同时影响隐私保护与分析统计,用于在数据可见性与安全性之间做权衡:
// 无论当前页面如何都不发送 referrer 信息
http
.get('/api/data', {
referrerPolicy: 'no-referrer',
})
.subscribe();
// 只发送 origin(例如 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 的 origin(协议、主机、端口),省略路径与查询串。'origin-when-cross-origin':同源请求发送完整 URL,跨域请求只发送 origin。'same-origin':同源请求发送完整 URL,跨域请求不发送 referrer。'strict-origin':仅当协议安全级别未降级时(例如 HTTPS→HTTPS)才只发送 origin;发生降级时省略 referrer。'strict-origin-when-cross-origin':浏览器默认行为。同源请求发送完整 URL,未降级的跨域请求发送 origin,发生降级时省略 referrer。'unsafe-url':总是发送完整 URL(含路径与查询串)。这可能泄露敏感数据,请谨慎使用。
TIP:对隐私敏感的请求,优先选择
'no-referrer'、'origin'或'strict-origin-when-cross-origin'这类保守取值。
完整性校验(Integrity)
integrity 选项通过提供响应内容的加密哈希值来校验响应未被篡改,在从 CDN 加载脚本等资源时尤其有用:
// 使用 SHA-256 哈希校验响应完整性
http
.get('/api/script.js', {
integrity: 'sha256-ABC123...',
responseType: 'text',
})
.subscribe((script) => {
// 脚本内容已按哈希完成校验
});
IMPORTANT:
integrity要求响应内容与提供的哈希精确匹配。内容不匹配时请求将以网络错误失败。 TIP:从外部来源加载关键资源时使用 Subresource Integrity(SRI)确保其未被修改,哈希可用openssl等工具生成。
HTTP Observable 的底层行为
HttpClient 的每个请求方法都会构造并返回一个目标响应类型的 Observable。理解这些 Observable 的工作方式对正确使用 HttpClient 至关重要:
- Cold Observable(冷可观察对象):
HttpClient产生的是 RxJS 所谓的 coldObservable——在订阅之前不会真正发起任何请求,只有订阅后才把请求派发到服务器。对同一个Observable多次订阅会触发多次后端请求,且每次订阅相互独立。
TIP:可以把
HttpClient的Observable视作真实服务器请求的"蓝图(blueprints)"。
-
取消订阅即中止请求:一旦订阅,取消订阅会中止进行中的请求。若该
Observable经由async管道订阅,当用户导航离开当前页面时请求会被自动取消;若配合switchMap这类组合操作符,取消还会清理掉陈旧的请求。 -
自动完成:响应返回后,来自
HttpClient的Observable通常会完成(complete,尽管拦截器可能影响这一点)。由于这种自动完成,即使不清理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';
@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(): void {
effect(() => {
this.user$ = this.userService.getUser(this.userId());
});
}
}
这里把请求职责收敛到服务层、把订阅职责交给 async 管道,既便于复用与测试(可参考 testing.md 对 HttpClient 的测试手法),又能利用上一节所述的取消订阅机制自动中止请求。
小结
HttpClient 的请求 API 围绕一套统一的 options 模型展开:responseType 决定响应解析方式,observe 决定观察粒度(响应体/完整响应/事件流),params/headers 以不可变对象管理 URL 参数与请求头,timeout 与 RxJS 错误处理操作符配合应对失败场景,而 fetch 后端专属的 keepalive、cache、priority、mode、redirect、credentials、referrer、referrerPolicy、integrity 等选项则提供了接近原生 Fetch API 的精细化控制。对各项选项语义与底层实现的完整把握,参见 making-requests.md 原文,以及 overview.md、interceptors.md 等 HTTP 指南系列文档;若希望了解源码级细节,可继续阅读 request.ts、params.ts、headers.ts、client.ts 与 fetch.ts。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00