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> <base-config cleartextTrafficPermitted="true" /> </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=WiFi
return 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中获取存储的Token
const 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); // 存储Token
Toast.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 证书验证)进一步优化,让网络请求更健壮、更安全。