HarmonyOS6.0开发实战:HTTP 网络请求与 API 交互全指南
Hello!我是小邢哥,专注编程实战技巧分享,深耕AI与机器学习领域多年,致力于把复杂技术拆解成易懂教程,陪大家在AI时代稳步进阶。
在鸿蒙应用开发中,网络请求是连接 “本地应用” 与 “远程服务” 的核心桥梁 —— 无论是拉取后端 API 数据(如用户信息、商品列表)、上传文件,还是调用第三方服务(如天气接口、地图接口),都依赖 HTTP 协议实现。
一、HTTP 协议:鸿蒙网络请求的 “底层规则”
HTTP 作为应用层协议,是鸿蒙网络请求的基础,但在鸿蒙开发中,我们需重点关注其与 “鸿蒙系统特性” 结合的部分 —— 比如协议版本支持、无状态问题的鸿蒙解决方案、安全适配等。
1.1 鸿蒙开发中需关注的 HTTP 核心特性
回顾 HTTP 协议的核心特性,以下 4 点直接影响鸿蒙网络请求的实现:
| 核心特性 | 鸿蒙开发中的影响与适配 |
|---|---|
| 请求 - 响应模式 | 鸿蒙应用作为客户端,需主动发起 GET/POST 等请求;服务器被动响应后,需处理响应体(如 JSON 解析)、状态码(如 404/500 错误) |
| 无状态特性 | 鸿蒙应用需通过 “请求头携带 Token”(如 JWT)或 “Cookie 存储” 实现会话跟踪(鸿蒙推荐用 Token,避免 Cookie 跨设备同步问题) |
| 可扩展性 | 鸿蒙支持自定义请求头(如Authorization: Bearer xxx)、HTTP/2 协议(默认开启,提升传输效率)、HTTPS 加密(强制要求,HTTP 需额外配置) |
| 依赖 TCP | 鸿蒙的@ohos.net.http模块已封装 TCP 连接管理,开发者无需关心底层连接建立 / 断开,只需关注请求参数与响应处理 |
1.2 鸿蒙对 HTTP 的特殊适配
-
协议版本支持:鸿蒙 4.0 及以上版本默认支持 HTTP/1.1、HTTP/2,部分设备支持 HTTP/3(基于 QUIC),无需额外配置即可享受高性能传输;
-
安全限制:鸿蒙默认禁止明文 HTTP 请求(http://),需在module.json5中配置 “网络安全策略” 才能使用;
-
权限管控:所有网络请求必须申请INTERNET权限,否则会直接抛出权限异常。

二、前置准备:鸿蒙网络请求的 “准入配置”
在写一行网络请求代码前,必须完成权限配置和安全配置(针对 HTTP),这是鸿蒙系统的强制要求,也是新手最易踩坑的点。
2.1 配置网络权限(必做)
鸿蒙应用需在module.json5(模块配置文件)中声明ohos.permission.INTERNET权限,否则无法发起任何网络请求。
配置步骤:
-
打开项目的src/main/module.json5文件;
-
在module -> reqPermissions数组中添加权限声明:
{"module": {"name": "entry","type": "entry","description": "鸿蒙网络请求示例","mainElement": "EntryAbility","deviceTypes": ["phone", "tablet"],// 网络权限配置"reqPermissions": [{"name": "ohos.permission.INTERNET", // 网络权限"reason": "需要访问网络获取API数据", // 权限申请理由(用户可见)"usedScene": {"ability": ["EntryAbility"],"when": "always" // 始终需要该权限}}],// 其他配置...}
}
注意:鸿蒙 6.0 及以上版本中,usedScene.when支持inuse(仅使用时申请),更符合隐私规范,建议优先使用。
2.2 明文 HTTP 请求配置(可选)
鸿蒙默认只允许https://请求(加密传输),若需调用http://接口(如测试环境接口),需额外配置 “网络安全策略”:
配置步骤:
-
在src/main/resources/rawfile目录下创建network_security_config.xml文件(若rawfile不存在则新建);
-
写入安全配置,允许指定域名的 HTTP 请求:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><!-- 允许所有HTTP请求(测试环境用,生产环境禁止) --><base-config cleartextTrafficPermitted="true" /><!-- 或仅允许指定域名(更安全) --><!-- <domain-config cleartextTrafficPermitted="true"><domain includeSubdomains="true">api.test.com</domain></domain-config> -->
</network-security-config>
-
在module.json5的module -> metaData中引用该配置:
{"module": {// 其他配置..."metaData": {"customData": [{"name": "ohos.application.networkSecurityConfig","value": "rawfile/network_security_config.xml"}]}}
}
警告:生产环境必须使用 HTTPS(https://),禁止配置cleartextTrafficPermitted="true",避免数据泄露风险。
三、核心实战:鸿蒙 HTTP 请求 API 全解析
鸿蒙提供@ohos.net.http模块实现 HTTP 请求,支持 GET(查询)、POST(提交)、PUT(更新)、DELETE(删除)等所有 HTTP 方法。以下从 “基础流程” 到 “实战示例” 逐步讲解,代码可直接在鸿蒙 Page 组件中运行。
3.1 鸿蒙 HTTP 请求的核心流程
无论哪种请求方法,鸿蒙 HTTP 请求都遵循 “5 步流程”,确保资源正确释放(避免内存泄漏):
-
导入模块:引入@ohos.net.http的核心类;
-
创建请求对象:实例化Http对象,管理单个请求的生命周期;
-
配置请求参数:设置 URL、方法、请求头、超时等;
-
发送请求并处理响应:获取状态码、响应头、响应体,解析数据(如 JSON);
-
销毁请求对象:请求结束后调用destroy(),释放资源。
3.2 实战 1:GET 请求(查询数据)
GET 请求用于 “获取资源”(如拉取列表、详情),参数通常拼接在 URL 后(如https://api.xxx.com/list?page=1&size=10)。
完整代码示例(拉取图片 API):
import http from '@ohos.net.http';
import { JsonUtils } from '../utils/JsonUtils'; // 复用前文封装的JSON工具类
import { Column, Button, Text, Progress, Toast } from '@ohos.arkui-components';@Entry
@Component
struct HttpGetDemo {// 状态管理:加载中、请求结果、错误信息@State isLoading: boolean = false;@State result: string = '';@State errorMsg: string = '';build() {Column({ space: 20 }) {Text('HTTP GET请求示例').fontSize(18).fontWeight(FontWeight.Bold)// 加载状态显示if (this.isLoading) {Progress({ value: 50, total: 100, type: ProgressType.CIRCULAR })Text('正在拉取数据...').fontSize(14).fontColor('#666')}// 错误信息显示if (this.errorMsg) {Text(`错误:${this.errorMsg}`).fontSize(14).fontColor('#ff4444').backgroundColor('#fff3f3').padding(10).borderRadius(5).width('90%')}// 结果显示(JSON格式化)if (this.result && !this.isLoading && !this.errorMsg) {Text('拉取结果:').fontSize(14).fontWeight(FontWeight.Bold).width('90%').textAlign(TextAlign.Start)Text(this.result).fontSize(12).backgroundColor('#f5f5f5').padding(10).borderRadius(5).width('90%').textAlign(TextAlign.Start).whiteSpace(WhiteSpace.PreWrap) // 保留换行}// 发起GET请求按钮Button('发起GET请求(拉取女生图片API)').width('90%').backgroundColor('#007DFF').fontColor('#fff').onClick(() => this.sendGetRequest())}.height('100%').padding(20).backgroundColor('#fff')}/*** 发送GET请求:拉取https://api.52vmy.cn/api/img/tu/girl接口数据*/private async sendGetRequest() {// 1. 重置状态this.isLoading = true;this.errorMsg = '';this.result = '';// 2. 创建HTTP请求对象let httpRequest: http.HttpRequest = http.createHttp();try {// 3. 配置请求参数并发送请求// 注意:GET参数可直接拼接在URL后,或通过params配置(鸿蒙会自动拼接)const response = await httpRequest.request('https://api.52vmy.cn/api/img/tu/girl', // 请求URL{method: http.RequestMethod.GET, // 请求方法header: {'Content-Type': 'application/json', // 告知服务器响应数据为JSON'User-Agent': 'HarmonyOS-App/1.0' // 自定义请求头(标识应用)},connectTimeout: 5000, // 连接超时:5秒(默认30秒)readTimeout: 5000 // 读取超时:5秒(默认30秒)// GET参数可选配置(与URL拼接等效)// params: {// count: 1 // 示例:请求1条数据(需接口支持)// }});// 4. 处理响应:先判断状态码if (response.responseCode === 200) {// 响应体是ArrayBuffer,需转为字符串const responseBody = String.fromCharCode.apply(null, new Uint8Array(response.result as ArrayBuffer));// 用JSON工具类格式化(便于显示)this.result = JsonUtils.stringify(JSON.parse(responseBody), 2);Toast.show({ message: 'GET请求成功' });} else {// 非200状态码(如404、500)this.errorMsg = `请求失败,状态码:${response.responseCode}`;}} catch (error) {// 5. 捕获异常(如网络断开、超时)this.errorMsg = `请求异常:${(error as Error).message}`;} finally {// 6. 无论成功/失败,都销毁请求对象(释放资源)httpRequest.destroy();this.isLoading = false;}}
}
3.3 实战 2:POST 请求(提交数据)
POST 请求用于 “提交资源”(如登录、上传表单、创建数据),参数通常放在 “请求体” 中(而非 URL),支持application/json(JSON 格式)、application/x-www-form-urlencoded(表单格式)两种常见类型。
2.1 POST 提交 JSON 格式(最常用)
适用于后端接口要求 “JSON 参数” 的场景(如登录接口):
/*** 发送POST请求(JSON格式参数)* 示例:模拟登录接口(假设接口为https://api.xxx.com/login)*/
private async sendPostJsonRequest() {this.isLoading = true;this.errorMsg = '';this.result = '';let httpRequest: http.HttpRequest = http.createHttp();try {// 1. 准备JSON参数(登录账号密码)const loginParams = {username: 'harmony_dev',password: '123456' // 实际开发中需加密传输,禁止明文!};// 2. 发送POST请求const response = await httpRequest.request('https://api.xxx.com/login', // 模拟登录接口URL{method: http.RequestMethod.POST,header: {'Content-Type': 'application/json', // 关键:声明参数为JSON格式'Authorization': 'Basic dGVzdDp0ZXN0' // 若接口需要基础认证,可添加},connectTimeout: 5000,readTimeout: 5000,// 3. 把JSON参数转为字符串,放入请求体extraData: JSON.stringify(loginParams)});// 4. 处理响应if (response.responseCode === 200) {const responseBody = String.fromCharCode.apply(null, new Uint8Array(response.result as ArrayBuffer));const responseJson = JSON.parse(responseBody);// 示例:提取Token(登录成功后后端返回)const token = responseJson.data.token;// 存储Token(后续请求携带,实现会话跟踪)// PreferencesUtil.put('user_token', token); // 可封装Preferences工具类this.result = JsonUtils.stringify(responseJson, 2);Toast.show({ message: `登录成功,Token:${token.substring(0, 10)}...` });} else {this.errorMsg = `登录失败,状态码:${response.responseCode}`;}} catch (error) {this.errorMsg = `登录异常:${(error as Error).message}`;} finally {httpRequest.destroy();this.isLoading = false;}
}
2.2 POST 提交表单格式
适用于后端接口要求 “表单参数” 的场景(如传统 Web 接口):
/*** 发送POST请求(表单格式参数)* 示例:模拟用户注册接口*/
private async sendPostFormRequest() {this.isLoading = true;this.errorMsg = '';this.result = '';let httpRequest: http.HttpRequest = http.createHttp();try {// 1. 准备表单参数(转为URL编码格式)const formParams = new URLSearchParams();formParams.append('username', 'new_harmony_user');formParams.append('email', 'user@example.com');formParams.append('password', 'encrypted_pwd'); // 实际开发需加密// 2. 发送POST请求const response = await httpRequest.request('https://api.xxx.com/register', // 模拟注册接口URL{method: http.RequestMethod.POST,header: {// 关键:声明参数为表单格式'Content-Type': 'application/x-www-form-urlencoded',},connectTimeout: 5000,readTimeout: 5000,// 3. 表单参数转为字符串,放入请求体extraData: formParams.toString()});// 4. 处理响应if (response.responseCode === 200) {const responseBody = String.fromCharCode.apply(null, new Uint8Array(response.result as ArrayBuffer));this.result = JsonUtils.stringify(JSON.parse(responseBody), 2);Toast.show({ message: '注册成功' });} else {this.errorMsg = `注册失败,状态码:${response.responseCode}`;}} catch (error) {this.errorMsg = `注册异常:${(error as Error).message}`;} finally {httpRequest.destroy();this.isLoading = false;}
}
四、鸿蒙网络请求的 “避坑指南” 与高级特性
4.1 常见错误与解决方案
| 错误场景 | 原因 | 解决方案 |
|---|---|---|
| 权限异常(permission denied) | 未配置INTERNET权限 | 在module.json5的reqPermissions中添加权限 |
| HTTP 请求被拒绝(cleartext not permitted) | 鸿蒙禁止明文 HTTP 请求 | 1. 改用 HTTPS;2. 配置network_security_config.xml |
| 超时异常(timeout) | 网络差、接口响应慢、超时时间设置过短 | 1. 延长connectTimeout/readTimeout(建议 5-10 秒);2. 增加网络状态判断 |
| 响应体解析失败(JSON.parse error) | 响应体不是 JSON 格式、接口返回错误信息(如 HTML) | 1. 先打印responseBody确认格式;2. 增加解析异常捕获 |
| 跨域问题(CORS error) | 前端直接调用非同源接口(浏览器环境常见,鸿蒙应用端少见) | 1. 后端配置 CORS 头(Access-Control-Allow-Origin);2. 通过后端代理转发请求 |
4.2 高级特性:网络状态监听
在发起请求前,先判断设备是否联网,可避免无效请求(提升用户体验)。鸿蒙提供@ohos.net.connection模块监听网络状态:
import connection from '@ohos.net.connection';/*** 检查设备是否联网* @returns true:联网;false:未联网*/
private async checkNetworkStatus(): Promise<boolean> {try {// 获取当前网络状态const netStatus = await connection.getActiveNetworkInfo();// 网络类型:1=无网络,2=移动网络,3=WiFireturn netStatus.type !== connection.NetworkType.NETWORK_TYPE_NONE;} catch (error) {console.error('检查网络状态失败:', error);return false;}
}// 在请求前调用
private async sendGetRequest() {// 先检查网络const isOnline = await this.checkNetworkStatus();if (!isOnline) {this.errorMsg = '当前无网络,请检查网络设置';Toast.show({ message: '无网络连接' });return;}// 后续请求逻辑...
}
4.3 高级特性:请求取消
若用户在请求过程中退出页面,需取消未完成的请求,避免内存泄漏或无效回调。鸿蒙的HttpRequest支持abort()方法:
// 1. 定义请求对象(改为类成员变量,便于外部访问)
private httpRequest: http.HttpRequest | null = null;// 2. 发起请求时赋值
private async sendGetRequest() {this.httpRequest = http.createHttp(); // 赋值给类成员try {const response = await this.httpRequest.request(/* 参数省略 */);// ...处理响应} catch (error) {// 捕获取消请求的异常(abort会触发error)if ((error as Error).message.includes('abort')) {console.log('请求已取消');this.errorMsg = '请求已取消';} else {this.errorMsg = `请求异常:${(error as Error).message}`;}}
}// 3. 页面销毁时取消请求(如退出Page组件)
aboutToDisappear() {if (this.httpRequest) {this.httpRequest.abort(); // 取消请求this.httpRequest.destroy(); // 销毁对象this.httpRequest = null;}
}
五、最佳实践:封装鸿蒙网络工具类
实际开发中,若每个组件都写重复的请求逻辑(创建对象、配置头、错误处理),会导致代码冗余。建议封装一个HttpUtil工具类,统一管理网络请求。
5.1 封装HttpUtil.ets
// utils/HttpUtil.ets
import http from '@ohos.net.http';
import connection from '@ohos.net.connection';
import { PreferencesUtil } from './PreferencesUtil'; // 封装的Preferences工具类(用于存Token)/*** 鸿蒙HTTP网络工具类:统一请求配置、错误处理、Token携带*/
export class HttpUtil {// 基础URL(不同环境可切换,如开发/测试/生产)private static BASE_URL = 'https://api.52vmy.cn';/*** 统一发送HTTP请求* @param url 接口路径(如'/api/img/tu/girl')* @param method 请求方法(GET/POST/PUT/DELETE)* @param params 请求参数(GET:拼接URL;POST:放入请求体)* @param header 自定义请求头(会覆盖默认头)* @returns 解析后的JSON响应体*/static async request<T>(url: string,method: http.RequestMethod = http.RequestMethod.GET,params?: Record<string, any>,header?: Record<string, string>): Promise<T> {// 1. 检查网络状态const isOnline = await this.checkNetwork();if (!isOnline) {throw new Error('当前无网络,请检查网络设置');}// 2. 创建请求对象const httpRequest = http.createHttp();// 完整URL(基础URL + 接口路径)const fullUrl = this.BASE_URL + url;try {// 3. 配置默认请求头(所有请求共享)const defaultHeader: Record<string, string> = {'Content-Type': 'application/json','User-Agent': 'HarmonyOS-App/1.0',// 自动携带Token(登录后存储,未登录则不携带)'Authorization': await this.getTokenHeader()};// 4. 合并自定义请求头(自定义会覆盖默认)const finalHeader = { ...defaultHeader, ...header };// 5. 处理请求参数let requestConfig: http.HttpRequestOptions = {method,header: finalHeader,connectTimeout: 8000,readTimeout: 8000};// 处理GET参数(拼接URL)if (method === http.RequestMethod.GET && params) {const queryStr = new URLSearchParams(params).toString();requestConfig.url = queryStr ? `${fullUrl}?${queryStr}` : fullUrl;}// 处理POST/PUT参数(放入请求体)if ([http.RequestMethod.POST, http.RequestMethod.PUT].includes(method) && params) {requestConfig.url = fullUrl;// 根据Content-Type判断参数格式if (finalHeader['Content-Type'] === 'application/x-www-form-urlencoded') {requestConfig.extraData = new URLSearchParams(params).toString();} else {requestConfig.extraData = JSON.stringify(params);}}// 6. 发送请求const response = await httpRequest.request(requestConfig.url || fullUrl, requestConfig);// 7. 处理响应状态码if (response.responseCode < 200 || response.responseCode >= 300) {throw new Error(`请求失败,状态码:${response.responseCode}`);}// 8. 解析响应体(ArrayBuffer → 字符串 → JSON)const responseBody = String.fromCharCode.apply(null, new Uint8Array(response.result as ArrayBuffer));const responseJson = JSON.parse(responseBody) as T;return responseJson;} catch (error) {// 统一错误处理(可添加日志上报)console.error(`HTTP请求失败(${fullUrl}):`, error);throw error; // 抛出错误,让调用方处理UI反馈} finally {// 9. 销毁请求对象httpRequest.destroy();}}/*** 检查网络状态*/private static async checkNetwork(): Promise<boolean> {try {const netStatus = await connection.getActiveNetworkInfo();return netStatus.type !== connection.NetworkType.NETWORK_TYPE_NONE;} catch (error) {console.error('检查网络状态失败:', error);return false;}}/*** 获取Token请求头(格式:Bearer xxx)*/private static async getTokenHeader(): Promise<string> {try {// 从Preferences中获取存储的Tokenconst token = await PreferencesUtil.get('user_token', '');return token ? `Bearer ${token}` : '';} catch (error) {console.error('获取Token失败:', error);return '';}}// 封装常用请求方法(简化调用)static async get<T>(url: string, params?: Record<string, any>, header?: Record<string, string>): Promise<T> {return this.request<T>(url, http.RequestMethod.GET, params, header);}static async post<T>(url: string, params?: Record<string, any>, header?: Record<string, string>): Promise<T> {return this.request<T>(url, http.RequestMethod.POST, params, header);}static async put<T>(url: string, params?: Record<string, any>, header?: Record<string, string>): Promise<T> {return this.request<T>(url, http.RequestMethod.PUT, params, header);}static async delete<T>(url: string, params?: Record<string, any>, header?: Record<string, string>): Promise<T> {return this.request<T>(url, http.RequestMethod.DELETE, params, header);}
}
5.2 工具类使用示例
// 组件中调用(简化90%代码)
private async useHttpUtil() {this.isLoading = true;try {// 1. 调用GET接口(拉取图片API)const getResult = await HttpUtil.get('/api/img/tu/girl', { count: 1 });this.result = JsonUtils.stringify(getResult, 2);// 2. 调用POST接口(模拟登录)// const loginResult = await HttpUtil.post('/login', {// username: 'harmony_dev',// password: 'encrypted_pwd'// });// await PreferencesUtil.put('user_token', loginResult.data.token); // 存储TokenToast.show({ message: '请求成功' });} catch (error) {this.errorMsg = (error as Error).message;Toast.show({ message: `请求失败:${this.errorMsg}` });} finally {this.isLoading = false;}
}
六、总结
鸿蒙 HTTP 网络请求的核心是 “@ohos.net.http模块 + 前置配置 + 统一封装”:
-
前置配置是基础:必须申请INTERNET权限,HTTPS 优先,HTTP 需额外配置安全策略;
-
核心流程要牢记:创建请求对象 → 配置参数 → 发送请求 → 处理响应 → 销毁对象,避免内存泄漏;
-
错误处理不可少:网络状态、超时、状态码、解析异常需全面覆盖;
-
封装工具类是关键:统一管理基础 URL、请求头、Token,减少重复代码,提升维护效率。
实际开发中,还需结合鸿蒙的 “分布式能力”(如跨设备网络共享)、“数据安全”(如 Token 加密存储、HTTPS 证书验证)进一步优化,让网络请求更健壮、更安全。
