Harmony os HTTP 网络访问(Network Kit 版)
写给未来的自己:这就是我以后写接口 / 下大文件 / 配 HTTPS 时要翻的那份笔记。
1. HTTP 能力总览
HarmonyOS 里访问网络主要靠 Network Kit 的 http 模块:
- 支持方法:
GET / POST / PUT / DELETE / OPTIONS / HEAD / TRACE / CONNECT - 提供两种请求方式:
httpRequest.request:普通请求,小数据量接口httpRequest.requestInStream:流式传输,大文件上传下载 + 进度
额外一条:
如果是"跨设备协同场景",可以考虑 Remote Communication Kit,它是更偏远程调用那条线,这里不展开。
2. 普通 HTTP 请求使用流程
2.1 导入模块 & 获取上下文
import { http } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
2.2 创建 HttpRequest 对象
一个
httpRequest对象 对应一个请求,用完直接销毁,不要复用。
const httpRequest = http.createHttp();
2.3 (可选)订阅响应头事件
headersReceive 会在 完整响应之前触发,适合先看 header 再决定后续处理方式。
httpRequest.on('headersReceive', (header) => {
console.info('header: ' + JSON.stringify(header));
});
从 API 8 开始,统一用
on('headersReceive', cb),不要再用老的headerReceive。
2.4 发起请求:request(url, options, callback)
常规 POST 请求笔记版示例:
httpRequest.request(
'EXAMPLE_URL',
{
method: http.RequestMethod.POST, // 默认是 GET
header: {
'Content-Type': 'application/json' // POST/PUT/DELETE/"" 默认就是 application/json
},
extraData: "data to send", // 请求体,格式跟后端约定
expectDataType: http.HttpDataType.STRING, // 指定返回数据类型,可选
usingCache: true, // 是否使用缓存(进程级),默认 true
priority: 1, // 并发优先级 [1,1000],越大越高
connectTimeout: 60000, // 连接超时,默认 60000ms
readTimeout: 60000, // 总读取超时(包含 DNS/连接/传输)
usingProtocol: http.HttpProtocol.HTTP1_1, // 协议版本,默认系统自动选
usingProxy: false, // 10+,是否使用系统或自定义代理
caPath: '/path/to/cacert.pem', // 10+,自定义 CA 证书路径
clientCert: { // 11+,客户端证书(双向 TLS)
certPath: '/path/to/client.pem',
keyPath: '/path/to/client.key',
certType: http.CertType.PEM,
keyPassword: 'passwordToKey'
},
// 11+,multipart/form-data 上传字段列表
multiFormDataList: [
{
name: 'Part1',
contentType: 'text/plain',
data: 'Example data',
remoteFileName: 'example.txt',
},
{
name: 'Part2',
contentType: 'text/plain',
filePath: `${context.filesDir}/fileName.txt`,
remoteFileName: 'fileName.txt',
},
{
name: 'Part3',
contentType: 'image/png',
filePath: `${context.filesDir}/fileName.png`,
remoteFileName: 'fileName.png',
},
],
},
(err: BusinessError, data: http.HttpResponse) => {
if (!err) {
console.info('Result: ' + JSON.stringify(data.result));
console.info('Code: ' + JSON.stringify(data.responseCode));
console.info('Header: ' + JSON.stringify(data.header));
console.info('Cookies: ' + JSON.stringify(data.cookies)); // 8+
httpRequest.destroy(); // ✅ 请求结束别忘了销毁
} else {
console.error('error: ' + JSON.stringify(err));
httpRequest.off('headersReceive'); // 取消订阅
httpRequest.destroy();
}
}
);
2.5 取消订阅 & 销毁
口诀:用完就
off + destroy,不要拖。
httpRequest.off('headersReceive');
httpRequest.destroy();
3. 流式 HTTP:大文件 & 进度控制
3.1 典型使用场景
- 下载大文件(视频、安装包、离线资源包)
- 上传大文件(日志、数据备份)
- 需要实时展示进度条
- 不想一次把数据全塞进内存
3.2 创建 HttpRequest
import { http } from '@kit.NetworkKit';
const httpRequest = http.createHttp(); // 一样:一任务一对象
3.3 订阅流式事件
let res = new ArrayBuffer(0);
// 实时接收数据块
httpRequest.on('dataReceive', (data: ArrayBuffer) => {
const newRes = new ArrayBuffer(res.byteLength + data.byteLength);
const resView = new Uint8Array(newRes);
resView.set(new Uint8Array(res));
resView.set(new Uint8Array(data), res.byteLength);
res = newRes;
console.info('res length: ' + res.byteLength);
});
// 全部数据接收完毕
httpRequest.on('dataEnd', () => {
console.info('No more data in response, data receive end');
});
// 下载进度
httpRequest.on('dataReceiveProgress', (info: http.DataReceiveProgressInfo) => {
console.info('dataReceiveProgress receiveSize:' + info.receiveSize + ', totalSize:' + info.totalSize);
});
// 上传进度
httpRequest.on('dataSendProgress', (info: http.DataSendProgressInfo) => {
console.info('dataSendProgress sendSize:' + info.sendSize + ', totalSize:' + info.totalSize);
});
实战里我会在
dataReceive里直接把数据写到文件,而不是累积在内存里,看场景选择。
3.4 发起流式请求:requestInStream
let streamInfo: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json'
},
extraData: "data to send",
expectDataType: http.HttpDataType.STRING,
usingCache: true,
priority: 1,
connectTimeout: 60000,
readTimeout: 60000, // 大文件建议加大一点
usingProtocol: http.HttpProtocol.HTTP1_1,
};
httpRequest.requestInStream('EXAMPLE_URL', streamInfo)
.then((code: number) => {
console.info('requestInStream OK!');
console.info('ResponseCode: ' + code);
destroyRequest(httpRequest);
})
.catch((err: Error) => {
console.error('requestInStream ERROR: ' + JSON.stringify(err));
destroyRequest(httpRequest);
});
3.5 统一封装销毁逻辑
function destroyRequest(httpRequest: http.HttpRequest) {
httpRequest.off('dataReceive');
httpRequest.off('dataSendProgress');
httpRequest.off('dataReceiveProgress');
httpRequest.off('dataEnd');
httpRequest.destroy();
}
4. HTTPS 证书相关配置笔记
场景:要么是自己做了证书锁定(pinning),要么是不想信任用户乱装的 CA,要么是测试环境想先跳过校验。
所有网络安全相关配置都写在:
src/main/resources/base/profile/network_config.json
4.1 证书锁定(Pinning)两种方式
方式 1:预置证书文件
- 支持
.crt/.pem格式; - 然后在
network_config.json里配置这个证书与域名的绑定关系。
简化例子(信任特定路径下证书):
{
"network-security-config": {
"base-config": {
"trust-anchors": [
{
"certificates": "/etc/security/certificates"
}
]
},
"domain-config": [
{
"domains": [
{
"include-subdomains": true,
"name": "example.com"
}
],
"trust-anchors": [
{
"certificates": "/data/storage/el1/bundle/entry/resources/resfile"
}
]
}
]
}
}
方式 2:预置证书公钥哈希(推荐更灵活)
- 到服务器拿到证书(OpenSSL 命令获取
.pem); - 从证书中提取公钥,算 SHA256,再 base64;
- 把这个
digest写入pin-set。
简化例子:
{
"network-security-config": {
"domain-config": [
{
"domains": [
{
"include-subdomains": true,
"name": "*.server.com"
}
],
"pin-set": {
"expiration": "2024-11-08",
"pin": [
{
"digest-algorithm": "sha256",
"digest": "FEDCBA987654321"
}
]
}
}
]
}
}
注意:证书链上任意一本证书变了,锁定都可能失败 → 服务器更新证书时,App 网络配置也要同步更新。
4.2 不信任用户安装的 CA 证书
默认:系统预置 CA + 用户安装 CA 都信任。
想要 更严安全策略:关掉两类用户 CA 信任。
{
"network-security-config": {
// ...你的其他配置
},
"trust-global-user-ca": false, // 不信任 MDM/管理员安装 CA
"trust-current-user-ca": false // 不信任当前用户自己装的 CA
}
5. 明文 HTTP(非 HTTPS)访问控制
开关大意:是否允许明文 HTTP,可按应用、域名、组件粒度控制。
5.1 优先级规则
- component-config > domain-config > base-config
- 越具体的优先级越高,后者覆盖前者。
5.2 配置示例
{
"network-security-config": {
"base-config": {
"cleartextTrafficPermitted": true // 应用默认允许 HTTP(API 20+)
},
"domain-config": [
{
"domains": [
{
"include-subdomains": true,
"name": "example.com"
}
],
"cleartextTrafficPermitted": false // 对 example.com 禁 HTTP
}
],
"component-config": {
"Network Kit": true, // Network Kit 支持禁止明文
"ArkWeb": false // ArkWeb 不支持禁止明文
}
}
}
字段速记:
cleartextTrafficPermitted: 是否允许 HTTP 明文传输(默认 true)Network Kit/ArkWeb: 这个组件是否参与"禁止明文"控制
6. 我自己的使用习惯小结
最后给自己留几条 checklist:
- 一请求一 HttpRequest,所有销毁逻辑统一封在工具函数里
- 普通接口用
request+expectDataType即可 - 大文件(下载/上传)一律走
requestInStream+ 进度回调 - 正式环境把
network_config.json安全相关配置补全:- 哪些域名要做证书锁定;
- 是否需要禁用用户 CA;
- 是否逐步禁用 HTTP 明文。
- 调试阶段策略可以宽一点,打包发布前再收紧