HarmonyOS @kit.NetworkKit 的 http 用法详解

@kit.NetworkKithttp 是鸿蒙官方网络模块,零依赖、直接 import 就能用。本章整理 http.createHttp() 的完整用法、各种请求场景(GET / POST JSON / POST 表单 / GET 带 query)、Promise 链式调用、错误处理、资源释放。

最小可运行示例

import { http } from '@kit.NetworkKit'

// 1. 创建请求实例
let httpRequest = http.createHttp()

// 2. 发请求
httpRequest.request('http://192.168.0.126:8080/goods/select', {
  method: http.RequestMethod.GET
}).then(resp => {
  // 3. 成功回调
  if (resp.responseCode === 200) {
    console.log('返回数据:', resp.result.toString())
  }
}).catch((err: Error) => {
  // 4. 失败回调
  console.error('网络错误:', err.message)
}).finally(() => {
  // 5. 释放资源 (必须做)
  httpRequest.destroy()
})
步骤 关键 API 必须性
创建实例 http.createHttp() 必须,每次请求都要新建
发请求 .request(url, options) 必须,返回 Promise
成功处理 .then(resp => ...) 至少要有一个 then
失败处理 .catch(err => ...) 推荐,不写错误会被吞
资源释放 .finally(() => destroy()) 必须,不调有资源泄漏风险

request 方法的参数

httpRequest.request(url: string, options: {
  method?: http.RequestMethod          // GET / POST / PUT / DELETE / ...
  header?: Object                       // 请求头(自定义 Content-Type / Authorization 等)
  extraData?: string | ArrayBuffer      // 请求体(POST / PUT 才需要)
  expectDataType?: http.HttpDataType    // 期望响应类型,影响 resp.result 的类型
  connectTimeout?: number               // 连接超时毫秒,默认 60000
  readTimeout?: number                  // 读取超时毫秒,默认 60000
  usingCache?: boolean                  // 是否使用缓存,默认 true
})

最常用的就是 method + header + extraData 三个。


http.RequestMethod 枚举

枚举值 说明
http.RequestMethod.GET 获取资源,不带 body
http.RequestMethod.POST 创建资源,带 JSON body
http.RequestMethod.PUT 完整更新,带 body
http.RequestMethod.DELETE 删除,可带可不带 body
http.RequestMethod.HEAD 只取响应头
http.RequestMethod.OPTIONS 查询服务器支持的方法(CORS 预检)

响应对象 resp 的结构

{
  responseCode: number   // HTTP 状态码: 200 / 404 / 500 等
  result: string | ArrayBuffer | Object   // 响应体
  resultType: http.HttpDataType   // 响应体的实际类型
  header: Object   // 响应头
  cookies: string   // 响应 Cookie
}
字段 类型 常见用法
responseCode number if (resp.responseCode === 200) 判断成功
result 联合类型 resp.result.toString() 转字符串 + JSON.parse 解析
header Object resp.header['content-type'] 看 MIME 类型

场景 1: GET 请求(无参数)

最简单的场景,直接拼 URL 即可。

function getList() {
  let httpRequest = http.createHttp()
  httpRequest.request('http://192.168.0.126:8080/goods/select', {
    method: http.RequestMethod.GET
  }).then(resp => {
    if (resp.responseCode === 200) {
      // 后端返回 JSON 数组 → 反序列化 + 类型断言
      const list = JSON.parse(resp.result.toString()) as Goods[]
      console.log('数据条数:', list.length)
    }
  }).finally(() => httpRequest.destroy())
}

场景 2: GET 请求带 query 参数

ArkTS 的 http 模块不会自动拼接 query 参数,需要自己拼到 URL 后面。

function deleteById(id: number) {
  let httpRequest = http.createHttp()
  httpRequest.request(`${BASE_URL}/goods/delete?id=${id}`, {
    method: http.RequestMethod.GET
  }).then(resp => { /* ... */ })
    .finally(() => httpRequest.destroy())
}

多个参数时:

const params = `?name=${encodeURIComponent(name)}&page=${page}&size=${size}`
httpRequest.request(`${BASE_URL}/goods/search${params}`, { ... })

必须 encodeURIComponent 包含中文、空格、特殊字符的参数,否则 URL 会出问题。简单的纯数字 / 英文 id 可以省略。


场景 3: POST 请求 + JSON body

RESTful 接口最主流的写法。三个关键参数:method / header / extraData

function insertGoods(name: string, price: number, desc: string, image: string) {
  let httpRequest = http.createHttp()
  httpRequest.request(`${BASE_URL}/goods/insert`, {
    method: http.RequestMethod.POST,
    header: { 'Content-Type': 'application/json' },
    extraData: JSON.stringify({ name, price, desc, image })
  }).then(resp => {
    if (resp.responseCode === 200) {
      console.log('插入成功')
    }
  }).catch((err: Error) => {
    console.error('网络错误:', err.message)
  }).finally(() => httpRequest.destroy())
}

关键点:

参数 说明
header['Content-Type'] 'application/json' 必须显式设置,否则后端可能拒收
extraData JSON.stringify({...}) 必须显式序列化,不能直接传对象

Spring Boot 的 @RequestBody Goods goods 注解默认只解析 JSON,所以前端 Content-Type 必须是 application/json 才能正确反序列化。


场景 4: POST 请求 + 表单数据

如果后端用 @RequestParam 而不是 @RequestBody,前端要发表单格式:

httpRequest.request(`${BASE_URL}/user/login`, {
  method: http.RequestMethod.POST,
  header: { 'Content-Type': 'application/x-www-form-urlencoded' },
  extraData: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`
})

数据格式是 key1=value1&key2=value2,和 query 字符串一样。


场景 5: 加请求头(Authorization / Token 等)

httpRequest.request(url, {
  method: http.RequestMethod.GET,
  header: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`,        // JWT 鉴权
    'X-Custom-Header': 'some-value'             // 任意自定义头
  }
})

场景 6: 设置超时

默认连接和读取都是 60 秒,实际开发中通常调短一点:

httpRequest.request(url, {
  method: http.RequestMethod.GET,
  connectTimeout: 5000,    // 5 秒连不上就失败
  readTimeout: 10000       // 10 秒没读完就失败
})

链式调用结构

整个请求的标准结构:

httpRequest.request(url, options)
  .then(resp => {
    // 处理响应
    if (resp.responseCode === 200) {
      // 成功路径
    } else {
      // HTTP 状态码非 200(如 404 / 500)
    }
  })
  .catch((err: Error) => {
    // 网络层失败(连接失败 / 超时 / DNS 解析失败 等)
    // 注意:HTTP 状态码 404 / 500 不会进 catch,而是进 then 的 else 分支
  })
  .finally(() => {
    httpRequest.destroy()
  })
进 then 的情况 进 catch 的情况
200 OK 网络不通 / 连接被拒绝
4xx 客户端错误 超时
5xx 服务器错误 DNS 解析失败
任何能拿到响应的情况 任何拿不到响应的情况

关键差异:HTTP 状态码不等于成功。只要服务器返回了响应,哪怕是 500,都会进 then。所以必须在 then 里手动判断 responseCode === 200


为什么必须 destroy()

http.createHttp() 创建的实例底层持有 socket / 内存 / 文件描述符等资源。如果不 destroy():

  • 单次请求看不出问题
  • 频繁创建不释放(比如列表刷新调几十次) → 资源泄漏 → 应用变卡 → 最终可能崩溃

最佳实践:用 .finally() 保证无论成功失败都调用 destroy()

.finally(() => {
  httpRequest.destroy()
})

完整封装(整合后端 CRUD 的常用模式)

const BASE_URL = 'http://192.168.0.126:8080'

// 通用 GET
function httpGet(path: string): Promise<string> {
  return new Promise((resolve, reject) => {
    let req = http.createHttp()
    req.request(`${BASE_URL}${path}`, { method: http.RequestMethod.GET })
      .then(resp => {
        if (resp.responseCode === 200) {
          resolve(resp.result.toString())
        } else {
          reject(new Error(`HTTP ${resp.responseCode}`))
        }
      })
      .catch(reject)
      .finally(() => req.destroy())
  })
}

// 通用 POST JSON
function httpPostJson(path: string, body: object): Promise<string> {
  return new Promise((resolve, reject) => {
    let req = http.createHttp()
    req.request(`${BASE_URL}${path}`, {
      method: http.RequestMethod.POST,
      header: { 'Content-Type': 'application/json' },
      extraData: JSON.stringify(body)
    })
      .then(resp => {
        if (resp.responseCode === 200) {
          resolve(resp.result.toString())
        } else {
          reject(new Error(`HTTP ${resp.responseCode}`))
        }
      })
      .catch(reject)
      .finally(() => req.destroy())
  })
}

// 调用
async function loadAndInsert() {
  try {
    const listText = await httpGet('/goods/select')
    const list = JSON.parse(listText) as Goods[]

    await httpPostJson('/goods/insert', {
      name: '新商品', price: 999, desc: '测试', image: 'xm17'
    })
  } catch (e) {
    console.error(e)
  }
}

学习项目可以不封装,直接用裸 API;但如果接口数量大于 5 个就建议封装,避免每个调用点都重复写 createHttp + destroy。


常见错误对照表

报错/现象 原因 修法
Permission Denied ohos.permission.INTERNET module.json5 加权限 + Clean + 重装
cleartext HTTP not permitted 鸿蒙默认禁明文 HTTP 改成 https,或加 cleartext 配置
connect timeout / read timeout 服务端未响应 / 防火墙拦截 浏览器自测 + 检查防火墙
HTTP 200 但 then 里 result 为空 后端没返回 body 看后端是不是直接 return 而没 return 数据
JSON.parse: unexpected token 后端返回的不是 JSON console.log(resp.result.toString()) 看实际返回
catch 里 err.message 是 null 鸿蒙内部错误未透出 看 IDE Log 里的 hilog,有详细堆栈
后端收不到 POST 参数 漏了 Content-Type header 必须显式设 'application/json'
后端收到 null 字段 字段名大小写不匹配 鸿蒙 extraData 里 key 要和后端 bean 完全一致

不止 http: rcp 模块更新更现代

鸿蒙还有一个 @hms.collaboration.rcp 模块(Remote Communication Kit),API 更现代(支持拦截器、自动 JSON、流式传输)。但是:

  • 需要 API 12+
  • 学习曲线比 http 高
  • 当前学习项目用 http 已经够了

如果以后要做正式项目、需要拦截器统一加 token / 统一错误处理,再考虑迁移到 rcp。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐