一、前言

前面系列教程覆盖了鸿蒙 UI 布局、路由、状态管理、本地存储、动画、权限、完整备忘录项目,而网络请求是 APP 对接后端接口、实现线上数据交互的核心能力,几乎所有商用 APP 都离不开网络调用。

OpenHarmony 原生提供两套网络请求方案,适配不同开发场景:

  1. fetch(推荐首选):语法简洁、异步原生支持、轻量无冗余,适合绝大多数业务接口请求
  2. http 模块:底层更灵活,支持请求超时配置、请求头精细管控、长连接,适合复杂接口、大文件上传下载

本文避开零散 demo,直接从网络权限配置、原生基础请求、全局统一请求封装、请求 / 响应拦截器、超时重试、统一异常捕获、文件上传下载、线上接口实战全覆盖,封装可直接用于企业项目的网络工具类,解决原生网络请求痛点:代码冗余、异常分散、无统一 loading、无超时处理、重复请求无法拦截。

二、前置准备:网络权限配置(必配)

2.1 module.json5 网络权限声明

鸿蒙应用默认禁止网络访问,必须手动声明网络权限,否则直接请求报错,无任何网络回调。

json

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET",
    "reason": "应用访问网络接口、获取线上数据",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  }
]

2.2 明文 http 请求配置(测试必备)

开发阶段大量接口为 http 明文协议,鸿蒙默认禁止明文网络请求,需要在 module.json5 增加网络安全配置,放开 http 限制:

json

"network": {
  "cleartextTraffic": true
}

三、两套原生网络 API 基础用法对比

3.1 Fetch 基础请求(GET/POST)

fetch 语法贴近前端原生语法,上手零成本,默认支持 Promise 异步调用,日常接口优先使用。

ets

// GET请求-获取列表数据
async function fetchGetDemo() {
  let res = await fetch('https://jsonplaceholder.typicode.com/todos/1')
  let data = await res.json()
  console.log('GET请求结果:', JSON.stringify(data))
}

// POST请求-提交表单数据
async function fetchPostDemo() {
  let res = await fetch('https://jsonplaceholder.typicode.com/posts',{
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      title: '鸿蒙网络测试',
      body: 'fetch post请求测试',
      userId: 1
    })
  })
  let data = await res.json()
  console.log('POST请求结果:', JSON.stringify(data))
}

3.2 http 模块基础请求(支持超时)

原生 fetch 不支持直接设置超时时间,http 模块原生支持超时、请求取消,稳定性更强。

ets

import http from '@ohos.net.http'

async function httpGetDemo() {
  // 创建http请求实例
  let httpReq = http.createHttp()
  // 设置超时时间 8000ms
  httpReq.requestTimeout = 8000
  let res = await httpReq.request('https://jsonplaceholder.typicode.com/todos/1',http.RequestMethod.GET)
  console.log('http请求结果:', res.result)
  // 请求结束销毁实例,避免内存泄漏
  httpReq.destroy()
}

四、企业级全局网络工具类(完整版,直接复制可用)

整合 GET/POST、统一请求头、超时拦截、异常捕获、全局 Loading、请求拦截、响应拦截,项目直接复用。

ets

import http from '@ohos.net.http'
import promptAction from '@ohos.promptAction'

// 全局基础域名
const BASE_URL = 'https://jsonplaceholder.typicode.com'
// 请求超时时间
const TIME_OUT = 8000

class HttpRequest {
  // 请求拦截器:统一添加token、请求头
  private requestInterceptor(options: any) {
    options.header = {
      'Content-Type': 'application/json;charset=UTF-8',
      // 自动携带登录token
      token: AppStorage.get('token') || '',
      ...options.header
    }
    return options
  }

  // 响应拦截器:统一处理状态码、后端错误码
  private responseInterceptor(res: http.HttpResponse): any {
    // 网络状态码判断
    if (res.responseCode !== 200) {
      promptAction.showToast({message:'网络请求异常,请稍后重试'})
      return null
    }
    return res.result
  }

  // 核心请求方法
  async request(url:string, method:http.RequestMethod, data:any = {}, options:any = {}) {
    // 执行请求拦截
    let reqOptions = this.requestInterceptor(options)
    // 拼接完整接口地址
    let fullUrl = BASE_URL + url
    // 创建请求实例
    let httpClient = http.createHttp()
    httpClient.requestTimeout = TIME_OUT

    try {
      promptAction.showLoading({message:'加载中...'})
      let res = await httpClient.request(fullUrl, method, {
        extraData: data,
        header: reqOptions.header
      })
      promptAction.dismissLoading()
      // 响应拦截处理
      return this.responseInterceptor(res)
    } catch (err) {
      promptAction.dismissLoading()
      // 统一捕获超时、断网、服务器异常
      promptAction.showToast({message:'网络异常或请求超时'})
      console.error('网络请求失败:',err)
      return null
    } finally {
      // 销毁实例,释放资源
      httpClient.destroy()
    }
  }

  // 封装GET请求
  get(url:string, params?:any) {
    return this.request(url, http.RequestMethod.GET, params)
  }

  // 封装POST请求
  post(url:string, data?:any) {
    return this.request(url, http.RequestMethod.POST, data)
  }
}

// 全局单例导出
export default new HttpRequest()

五、页面直接调用(极简调用方式)

ets

import HttpRequest from '../utils/HttpRequest'

@Entry
@Component
struct NetworkDemoPage {
  @State resultText:string = ''

  // 调用GET接口
  async getNetData() {
    let res = await HttpRequest.get('/todos/1')
    this.resultText = JSON.stringify(res)
  }

  // 调用POST接口
  async postNetData() {
    let res = await HttpRequest.post('/posts',{
      title:'鸿蒙网络封装测试',
      content:'统一请求工具类调用成功'
    })
    this.resultText = JSON.stringify(res)
  }

  build() {
    Column({space:20}) {
      Button('发起GET网络请求').onClick(()=>this.getNetData())
      Button('发起POST网络请求').onClick(()=>this.postNetData())
      Text(this.resultText).fontSize(14).padding(15).width('90%')
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

六、进阶功能:请求防抖 + 重复请求拦截

防止用户快速点击按钮,短时间多次发起相同请求,造成接口重复调用:

ets

// 存储正在请求的接口地址
let pendingUrlList:string[] = []

// 请求前拦截重复请求
function checkPending(url:string):boolean {
  if(pendingUrlList.includes(url)){
    promptAction.showToast({message:'请勿重复请求'})
    return true
  }
  pendingUrlList.push(url)
  return false
}

// 请求结束移除记录
function removePending(url:string) {
  let index = pendingUrlList.indexOf(url)
  if(index > -1){
    pendingUrlList.splice(index,1)
  }
}

七、网络请求开发踩坑大全

  1. 忘记配置网络权限:不报代码错误,接口始终无返回,必须配置ohos.permission.INTERNET
  2. http 请求被拦截:真机默认禁止明文 http,必须开启cleartextTraffic:true
  3. http 实例不销毁:频繁请求不调用 destroy,会造成严重内存泄漏
  4. 无超时处理:网络差时页面一直 loading,必须统一配置超时时间
  5. 缺少异常捕获:断网、服务器崩溃直接页面白屏,必须 try-catch 全局捕获
  6. 重复请求:按钮高频点击重复发请求,需要做请求队列拦截

八、Fetch 与 Http 选型建议

  1. 日常业务接口:优先用 fetch,代码简洁,快速开发
  2. 需要超时 / 取消请求:必选 http 原生模块
  3. 企业项目统一开发:直接封装 http 工具类,统一拦截、统一异常、统一 loading,适配全业务
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐