一、前言

应用开发离不开网络交互,从首页商品、资讯加载到登录提交,都需要调用后端接口。OpenHarmony 原生使用fetch实现 http/https 网络请求,无需额外引入三方库,支持 GET、POST、请求头配置、JSON 解析。本文讲解基础请求、参数传递、异常捕获,搭配真实模拟接口,结合前面 Preferences、ArkUI 组件完成网络数据列表展示实战

开发前置:配置网络权限 项目目录 src/main/module.json5 → module.requestPermissions 添加网络权限:

json

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  }
]

二、Fetch 基础语法说明

fetch 返回 Promise 异步对象,分为两步:

  1. fetch (url, options) 发起请求,返回 Response 响应对象
  2. response.json () 解析 json 格式数据
  • GET:参数拼在 URL 末尾
  • POST:参数放在 body 中,需配置请求头Content-Type

三、GET 请求实战(无参 / 带参)

3.1 无参数 GET 获取接口数据

ets

@Entry
@Component
struct FetchGetDemo {
  @State resText: string = ""

  // GET请求
  async requestGet() {
    try {
      // 公开测试JSON接口
      let resp = await fetch("https://jsonplaceholder.typicode.com/posts/1")
      let data = await resp.json()
      this.resText = JSON.stringify(data, null, 2)
    } catch (err) {
      this.resText = "请求异常:" + JSON.stringify(err)
    }
  }

  build() {
    Column({ space: 15 }) {
      Button("发起GET请求").onClick(() => this.requestGet())
      Scroll() {
        Text(this.resText).fontSize(14).padding(10)
      }
      .width("100%")
      .layoutWeight(1)
    }
    .padding(20)
    .height("100%")
  }
}

3.2 GET 拼接请求参数

ets

async getWithParam() {
  let id = 5
  let url = `https://jsonplaceholder.typicode.com/posts/${id}`
  let res = await fetch(url)
  let result = await res.json()
  console.info("携带参数数据:", result.title)
}

四、POST 请求(提交表单 JSON 参数)

POST 多用于登录、提交表单,请求体使用 JSON 格式,固定请求头:Content-Type: application/json

ets

async requestPost() {
  let url = "https://jsonplaceholder.typicode.com/posts"
  // 请求参数
  let bodyData = {
    title: "OpenHarmony测试文章",
    body: "鸿蒙fetch网络开发",
    userId: 1
  }
  let options: RequestInit = {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(bodyData)
  }
  try {
    let resp = await fetch(url, options)
    let data = await resp.json()
    console.info("POST返回数据:", data)
  } catch (e) {
    console.error("请求失败", e)
  }
}

五、封装通用网络请求工具类

项目中统一封装请求,统一处理异常、请求头,便于后期维护:

ets

class HttpUtil {
  // 通用GET
  static async get(url: string) {
    let res = await fetch(url)
    if (!res.ok) throw new Error("接口异常:" + res.status)
    return await res.json()
  }
  // 通用POST
  static async post(url: string, data: Object) {
    let opt: RequestInit = {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data)
    }
    let res = await fetch(url, opt)
    if (!res.ok) throw new Error("接口异常:" + res.status)
    return await res.json()
  }
}

六、综合实战:网络数据渲染 List 列表(融合懒加载 LazyForEach)

业务:请求接口批量获取文章数据,通过 LazyForEach 渲染列表,下拉刷新重新请求接口。

ets

// 懒加载数据源
class ArticleDataSource implements ICollectionData {
  private list: Array<Object> = []
  setData(arr: Object[]) {
    this.list = arr
  }
  getTotalCount(): number { return this.list.length }
  getData(index: number): Object { return this.list[index] }
  setDataChangeListener(listener: DataChangeListener): void {}
}

@Entry
@Component
struct NetListPage {
  @State isRefresh: boolean = false
  dataSource: ArticleDataSource = new ArticleDataSource()

  // 请求接口刷新数据
  async loadArticle() {
    this.isRefresh = true
    try {
      let list = await HttpUtil.get("https://jsonplaceholder.typicode.com/posts?_limit=20")
      this.dataSource.setData(list)
    } catch (err) {
      console.error("数据加载失败", err)
    } finally {
      this.isRefresh = false
    }
  }

  aboutToAppear() {
    this.loadArticle()
  }

  build() {
    Column() {
      Text("资讯列表").fontSize(22).fontWeight(Font.Bold).padding(15)
      List() {
        LazyForEach(this.dataSource, (item: any) => {
          ListItem() {
            Column({ space:6 }) {
              Text(item.title).fontSize(16).fontWeight(FontWeight.Bold)
              Text(item.body).fontSize(14).fontColor("#666")
            }
            .width("100%")
            .padding(12)
            .backgroundColor("#fff")
            .borderRadius(8)
            .margin({ bottom:8 })
          }
        })
      }
      .width("100%")
      .layoutWeight(1)
      .refresh({refreshing: this.isRefresh})
      .onRefresh(()=>this.loadArticle())
    }
    .width("100%")
    .backgroundColor("#F5F5F5")
  }
}

七、拓展实战:登录页网络提交 + 本地存储(融合 Preferences)

点击登录发起 POST 请求,登录成功后使用 Preferences 保存登录状态。

ets

import preferences from '@ohos.data.preferences'
import common from '@ohos.app.ability.common'

@Entry
@Component
struct NetLoginPage {
  @State account: string = ""
  @State pwd: string = ""
  context = getContext(this) as common.UIAbilityContext

  // 登录请求
  async doLogin() {
    let loginParam = {
      username: this.account,
      password: this.pwd
    }
    try {
      // 模拟登录接口
      let res = await HttpUtil.post("https://jsonplaceholder.typicode.com/users", loginParam)
      // 登录成功,持久化保存登录标识
      let pre = await preferences.getPreferences(this.context, "user_login")
      await pre.put("isLogin", true)
      await pre.put("userName", this.account)
      await pre.flush()
      AlertDialog.show({title:"提示",message:"登录成功!"})
    } catch (err) {
      AlertDialog.show({title:"提示",message:"登录失败"})
    }
  }

  build() {
    Column({ space:20 }) {
      TextInput({placeholder:"账号"}).width("90%").onChange(v=>this.account=v)
      TextInput({placeholder:"密码"}).type(InputType.Password).width("90%").onChange(v=>this.pwd=v)
      Button("登录").width("90%").backgroundColor("#007DFF").onClick(()=>this.doLogin())
    }
    .width("100%").height("100%").justifyContent(FlexAlign.Center).padding(20)
  }
}

八、开发规范与踩坑总结

  1. 权限必开:忘记配置 INTERNET 会直接请求报错;
  2. 异常捕获:所有网络请求必须套try/catch,避免断网闪退;
  3. 数据格式:POST 传 JSON 必须配置 Header,否则后端无法解析参数;
  4. 频繁请求优化:页面销毁时取消无用请求,避免内存浪费;
  5. HTTPS 优先:OpenHarmony 默认支持 https,http 部分设备需要额外配置白名单。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐