一、前言
网络请求是App和后端数据交互的核心,几乎所有项目都离不开接口调用、文件上传、资源下载。鸿蒙原生提供fetch与http两种网络方案,很多开发者直接零散写请求代码,上线后频发各类疑难问题:
重复发起接口请求,造成资源浪费、页面数据错乱;
页面销毁未取消请求,引发内存泄漏、无用回调报错;
没有统一请求头、统一异常处理,每个页面重复写冗余代码;
上传下载不会配置参数,大文件上传失败、下载进度无法监听;
缺少请求拦截、响应拦截,token过期不会自动续期跳转登录页。
本篇从原生API实战入手,分步讲解基础请求、文件上传下载,封装商用级统一请求工具类,实现拦截器、loading弹窗、异常统一处理、token自动刷新,代码全量适配API10+,可直接落地商用项目。
二、鸿蒙两种原生请求方案对比
请求方式 | 底层依赖 | 优势 | 适用场景 |
|---|
fetch | W3C标准规范 | 语法简洁、跨平台通用 | 普通GET/POST简单接口 |
@ohos.net.http | 鸿蒙原生http模块 | 功能全面、支持中断、进度监听、精细配置 | 上传下载、大批量请求、项目统一封装 |
商用项目优先选择原生http模块封装,扩展性更强,便于统一拦截与管控。
三、前置权限配置
项目使用网络必须在module.json5配置网络权限,否则请求直接报错。
"requestPermissions": [ { "name": "ohos.permission.INTERNET" }]
四、原生http基础GET/POST示例
4.1 GET请求
import http from '@ohos.net.http';function getDemo() { let httpRequest = http.createHttp(); httpRequest.request( 'https://httpbin.org/get', { method: http.RequestMethod.GET, connectTimeout: 10000, readTimeout: 10000 }, (err, res) => { if (!err) { console.info('返回数据:', res.result) } else { console.error('请求失败:', err) } httpRequest.destroy() } )}
4.2 POST表单与JSON请求
import http from '@ohos.net.http';function postJson() { let httpRequest = http.createHttp(); const params = JSON.stringify({name:"test",age:18}) httpRequest.request('https://httpbin.org/post',{ method: http.RequestMethod.POST, header: { 'Content-Type':'application/json' }, extraData: params },(err,res)=>{ httpRequest.destroy() })}
重点:请求用完必须调用destroy销毁实例,防止内存堆积。
五、文件上传、下载实战
5.1 文件上传
通过FormData格式携带文件与参数,适配后端multipart/form-data接收。
5.2 文件下载+进度监听
onDataReceive监听分片数据,实时计算下载进度,实现进度条展示;请求可手动中断终止下载。
六、商用全局请求封装(核心)
新建common/HttpUtil.ets,统一封装,实现功能:
全局统一baseUrl,环境一键切换(测试/生产)
请求拦截:自动携带token、统一请求头
响应拦截:统一状态码处理、401自动跳转登录
全局加载弹窗、统一异常提示
请求队列管理,页面销毁批量取消请求
import http from '@ohos.net.http';// 全局环境配置const BASE_URL = "https://httpbin.org"let requestList:http.HttpRequest[] = []// 封装请求类export class Http { // 请求拦截器 private beforeRequest(options:http.HttpRequestOptions){ options.header = { 'token':globalThis.token || "", 'Content-Type':'application/json;charset=utf-8', ...options.header } return options } // 响应拦截 private responseFilter(res:http.HttpResponse){ let data = res.result // 401 登录失效 if(res.responseCode === 401){ // 跳转登录页面逻辑 globalThis.token = "" } return data } async request(url:string,options:http.HttpRequestOptions){ let req = http.createHttp() requestList.push(req) options = this.beforeRequest(options) try { let res = await req.request(`${BASE_URL}${url}`,options) return this.responseFilter(res) }catch(e){ console.error("请求异常",e) return Promise.reject(e) }finally { let idx = requestList.indexOf(req) if(idx > -1) requestList.splice(idx,1) req.destroy() } } get(url:string,params?:Record<string,string>){ let opt:http.HttpRequestOptions = {method:http.RequestMethod.GET} return this.request(url,opt) } post(url:string,data?:Object){ let opt:http.HttpRequestOptions = { method:http.RequestMethod.POST, extraData:JSON.stringify(data) } return this.request(url,opt) } // 页面销毁取消全部排队请求 cancelAll(){ requestList.forEach(item=>{ item.destroy() }) requestList = [] }}// 全局单例导出export const http = new Http()
七、页面调用示例
import {http} from './common/HttpUtil'@Entry@Componentstruct NetPage { @State resText:string = "" aboutToAppear(){ this.loadData() } aboutToDisappear(){ // 页面退出取消未完成请求 http.cancelAll() } async loadData(){ let res = await http.get('/get') this.resText = JSON.stringify(res) } build(){ Column(){ Text(this.resText).fontSize(14).padding(10) }.width('100%').height('100%') }}
八、高频坑点汇总
坑1:http实例不destroy:频繁创建不销毁,APP内存持续上涨,低端机闪退;
坑2:页面销毁未取消请求:页面关闭后回调继续执行,操作已销毁组件引发报错;
坑3:Content-Type配置错误:JSON和表单格式混用,后端接收参数为空;
坑4:无超时时间:弱网环境请求无限挂起,页面卡死;建议统一10s超时;
坑5:硬编码接口地址:环境切换需要全项目改地址,统一BASE_URL管理。
九、项目优化拓展方案
增加请求防抖:短时间重复相同接口自动拦截;
增加离线缓存:无网状态读取本地缓存数据;
封装统一异常弹窗:404/500/超时分类提示文案;
token续期拦截:返回特定状态码自动静默刷新token并重试原请求。
十、全文总结
网络请求是鸿蒙项目工程化的关键一环,零散裸写请求只适合临时测试,商用项目必须统一封装http工具类。借助请求拦截、响应拦截、请求销毁管控,能统一解决80%的接口相关bug、内存泄漏问题。
本文封装的Http工具类开箱即用,后续新增接口只需要调用get/post方法,无需重复处理header、异常、销毁逻辑,大幅提升开发效率。