程序员Feri 2025-11-03 13:04:41 发布Hello!我是程序员Feri,专注编程实战技巧分享,深耕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@Componentstruct 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.etsimport 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,减少重复代码,提升维护效率。
暂无评论数据
发布
相关推荐
周正
303
0
周正
425
0
周正
95
0
周正
164
0
周正
449
0
程序员Feri
13 年编程老炮,华为开发者专家,北科大硕士,实战派技术人(开发/架构/教学/创业),拆解编程技巧、分享副业心得,记录程序员的进阶路,AI 时代一起稳稳向前。
帖子
提问
粉丝
【万字硬核】HarmonyOS 6.0 游戏开发终极指南:从渲染架构到 FFRT 并行优化全解析
2026-01-22 18:00:22 发布【万字硬核】深入剖析 HarmonyOS 6.0 的 V2 状态管理:从原理到实战的完整实操
2026-01-22 17:59:30 发布