Skip to content

网络请求

HarmonyOS 使用 @kit.NetworkKit 进行网络请求,支持 HTTP/HTTPS、WebSocket、文件上传下载以及网络状态监听。

前置配置

module.json5 中声明网络权限:

json5
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET",
        "reason": "$string:reason_internet",
        "usedScene": {}
      }
    ]
  }
}

提示

INTERNET 权限无需动态申请,声明后即可使用。

HTTP 请求

API 列表

API说明起始版本
http.createHttp()创建 HTTP 请求实例API 6
HttpRequest.request()发起网络请求API 6
HttpRequest.destroy()销毁请求实例,释放资源API 6
HttpRequest.on('headersReceive')监听响应头接收事件API 6
HttpRequest.off('headersReceive')取消监听响应头接收事件API 6

RequestMethod 枚举

枚举值说明
GETGET 请求
POSTPOST 请求
PUTPUT 请求
DELETEDELETE 请求
HEADHEAD 请求
OPTIONSOPTIONS 请求
TRACETRACE 请求
CONNECTCONNECT 请求

HttpDataType 枚举

枚举值说明
STRING返回字符串
OBJECT返回 JSON 对象
ARRAY_BUFFER返回 ArrayBuffer

GET 请求

typescript
import { http } from '@kit.NetworkKit'

async function fetchUser(userId: number): Promise<void> {
  const request = http.createHttp()

  try {
    const response = await request.request(`https://api.example.com/users/${userId}`, {
      method: http.RequestMethod.GET,
      header: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer your-token'
      },
      expectDataType: http.HttpDataType.STRING,
      connectTimeout: 10000,
      readTimeout: 10000
    })

    if (response.responseCode === 200) {
      const data = JSON.parse(response.result as string)
      console.info('用户信息:', JSON.stringify(data))
    } else {
      console.error('请求失败:', response.responseCode)
    }
  } catch (error) {
    console.error('网络错误:', JSON.stringify(error))
  } finally {
    request.destroy()  // 必须销毁,避免内存泄漏
  }
}

POST 请求(JSON)

typescript
import { http } from '@kit.NetworkKit'

interface CreateUserRequest {
  name: string
  email: string
  age: number
}

async function createUser(user: CreateUserRequest): Promise<void> {
  const request = http.createHttp()

  try {
    const response = await request.request('https://api.example.com/users', {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'application/json'
      },
      extraData: JSON.stringify(user),
      expectDataType: http.HttpDataType.STRING
    })

    if (response.responseCode === 201) {
      const result = JSON.parse(response.result as string)
      console.info('创建成功:', JSON.stringify(result))
    }
  } catch (error) {
    console.error('创建失败:', JSON.stringify(error))
  } finally {
    request.destroy()
  }
}

表单请求

typescript
async function uploadForm(): Promise<void> {
  const request = http.createHttp()

  const formData = 'name=张三&age=25'

  try {
    const response = await request.request('https://api.example.com/form', {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      extraData: formData,
      expectDataType: http.HttpDataType.STRING
    })

    console.info('响应:', response.result)
  } finally {
    request.destroy()
  }
}

监听响应头

typescript
import { http } from '@kit.NetworkKit'

async function requestWithHeaderListener(): Promise<void> {
  const request = http.createHttp()

  // 监听响应头
  request.on('headersReceive', (header) => {
    console.info('响应头:', JSON.stringify(header))
  })

  try {
    const response = await request.request('https://api.example.com/data', {
      method: http.RequestMethod.GET
    })
    console.info('响应体:', response.result)
  } finally {
    request.off('headersReceive')
    request.destroy()
  }
}

文件上传

使用 HTTP 上传

typescript
import { http } from '@kit.NetworkKit'

async function uploadFile(filePath: string): Promise<void> {
  const request = http.createHttp()

  try {
    const response = await request.request('https://api.example.com/upload', {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'multipart/form-data'
      },
      // 使用文件路径上传
      extraData: {
        "file": filePath,
        "name": "avatar.jpg"
      },
      expectDataType: http.HttpDataType.STRING
    })

    console.info('上传结果:', response.result)
  } finally {
    request.destroy()
  }
}

上传进度监听

typescript
import { http } from '@kit.NetworkKit'

async function uploadWithProgress(filePath: string): Promise<void> {
  const request = http.createHttp()

  try {
    // 监听上传进度
    request.on('dataReceiveProgress', (receiveData) => {
      console.info('上传进度:', receiveData.receiveSize, '/', receiveData.totalSize)
    })

    const response = await request.request('https://api.example.com/upload', {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'multipart/form-data'
      },
      extraData: {
        "file": filePath,
        "name": "avatar.jpg"
      },
      expectDataType: http.HttpDataType.STRING
    })

    console.info('上传完成:', response.result)
  } finally {
    request.off('dataReceiveProgress')
    request.destroy()
  }
}

文件下载

使用 request 下载

typescript
import { http } from '@kit.NetworkKit'
import { fileIo } from '@kit.CoreFileKit'

async function downloadFile(url: string, savePath: string): Promise<void> {
  const request = http.createHttp()

  try {
    const response = await request.request(url, {
      method: http.RequestMethod.GET,
      expectDataType: http.HttpDataType.ARRAY_BUFFER
    })

    if (response.responseCode === 200) {
      const file = fileIo.openSync(savePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY)
      fileIo.writeSync(file.fd, response.result as ArrayBuffer)
      fileIo.closeSync(file)
      console.info('下载完成:', savePath)
    }
  } catch (error) {
    console.error('下载失败:', JSON.stringify(error))
  } finally {
    request.destroy()
  }
}

下载进度监听

typescript
import { http } from '@kit.NetworkKit'
import { fileIo } from '@kit.CoreFileKit'

async function downloadWithProgress(url: string, savePath: string): Promise<void> {
  const request = http.createHttp()

  try {
    // 监听下载进度
    request.on('dataReceiveProgress', (receiveData) => {
      const progress = (receiveData.receiveSize / receiveData.totalSize) * 100
      console.info(`下载进度: ${progress.toFixed(2)}%`)
    })

    const response = await request.request(url, {
      method: http.RequestMethod.GET,
      expectDataType: http.HttpDataType.ARRAY_BUFFER
    })

    if (response.responseCode === 200) {
      const file = fileIo.openSync(savePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY)
      fileIo.writeSync(file.fd, response.result as ArrayBuffer)
      fileIo.closeSync(file)
      console.info('下载完成:', savePath)
    }
  } catch (error) {
    console.error('下载失败:', JSON.stringify(error))
  } finally {
    request.off('dataReceiveProgress')
    request.destroy()
  }
}

请求封装

建议封装统一的请求工具:

typescript
// utils/HttpUtil.ets
import { http } from '@kit.NetworkKit'

interface RequestOptions {
  method?: http.RequestMethod
  header?: Record<string, string>
  body?: object | string
  timeout?: number
}

class HttpUtil {
  private baseURL: string = 'https://api.example.com'
  private token: string = ''

  setToken(token: string) {
    this.token = token
  }

  async request<T>(url: string, options: RequestOptions = {}): Promise<T> {
    const request = http.createHttp()
    const fullURL = url.startsWith('http') ? url : `${this.baseURL}${url}`

    try {
      const header: Record<string, string> = {
        'Content-Type': 'application/json',
        ...options.header
      }

      if (this.token) {
        header['Authorization'] = `Bearer ${this.token}`
      }

      const response = await request.request(fullURL, {
        method: options.method || http.RequestMethod.GET,
        header,
        extraData: typeof options.body === 'string' ? options.body : JSON.stringify(options.body),
        expectDataType: http.HttpDataType.STRING,
        connectTimeout: options.timeout || 10000,
        readTimeout: options.timeout || 10000
      })

      if (response.responseCode >= 200 && response.responseCode < 300) {
        return JSON.parse(response.result as string) as T
      } else {
        throw new Error(`HTTP ${response.responseCode}: ${response.result}`)
      }
    } finally {
      request.destroy()
    }
  }

  get<T>(url: string, header?: Record<string, string>): Promise<T> {
    return this.request<T>(url, { method: http.RequestMethod.GET, header })
  }

  post<T>(url: string, body?: object, header?: Record<string, string>): Promise<T> {
    return this.request<T>(url, { method: http.RequestMethod.POST, body, header })
  }

  put<T>(url: string, body?: object, header?: Record<string, string>): Promise<T> {
    return this.request<T>(url, { method: http.RequestMethod.PUT, body, header })
  }

  delete<T>(url: string, header?: Record<string, string>): Promise<T> {
    return this.request<T>(url, { method: http.RequestMethod.DELETE, header })
  }
}

export const httpUtil = new HttpUtil()

使用示例:

typescript
import { httpUtil } from '../utils/HttpUtil'

interface User {
  id: number
  name: string
  email: string
}

// GET 请求
const user = await httpUtil.get<User>('/users/1')

// POST 请求
const newUser = await httpUtil.post<User>('/users', {
  name: '张三',
  email: 'zhangsan@example.com'
})

WebSocket

API 列表

API说明起始版本
webSocket.createWebSocket()创建 WebSocket 实例API 7
WebSocket.connect()连接 WebSocket 服务器API 7
WebSocket.send()发送消息API 7
WebSocket.close()关闭连接API 7
WebSocket.on('open')监听连接打开事件API 7
WebSocket.on('message')监听消息接收事件API 7
WebSocket.on('close')监听连接关闭事件API 7
WebSocket.on('error')监听错误事件API 7

基础用法

typescript
import { webSocket } from '@kit.NetworkKit'

class WebSocketClient {
  private ws: webSocket.WebSocket | null = null

  connect(url: string) {
    this.ws = webSocket.createWebSocket()

    this.ws.on('open', () => {
      console.info('WebSocket 连接成功')
    })

    this.ws.on('message', (err, data) => {
      console.info('收到消息:', data)
    })

    this.ws.on('close', () => {
      console.info('WebSocket 连接关闭')
    })

    this.ws.on('error', (err) => {
      console.error('WebSocket 错误:', JSON.stringify(err))
    })

    this.ws.connect(url)
  }

  send(message: string) {
    if (this.ws) {
      this.ws.send(message)
    }
  }

  close() {
    if (this.ws) {
      this.ws.close()
      this.ws = null
    }
  }
}

完整封装示例

typescript
import { webSocket } from '@kit.NetworkKit'

interface WebSocketOptions {
  url: string
  reconnect?: boolean
  reconnectInterval?: number
  maxReconnectAttempts?: number
}

class WebSocketManager {
  private ws: webSocket.WebSocket | null = null
  private options: WebSocketOptions
  private reconnectAttempts: number = 0
  private reconnectTimer: number | null = null
  private messageHandlers: Array<(data: string) => void> = []

  constructor(options: WebSocketOptions) {
    this.options = {
      reconnect: true,
      reconnectInterval: 3000,
      maxReconnectAttempts: 5,
      ...options
    }
  }

  connect(): void {
    this.ws = webSocket.createWebSocket()

    this.ws.on('open', () => {
      console.info('WebSocket 连接成功')
      this.reconnectAttempts = 0
    })

    this.ws.on('message', (err, data) => {
      if (err) {
        console.error('消息接收错误:', JSON.stringify(err))
        return
      }
      this.messageHandlers.forEach(handler => handler(data as string))
    })

    this.ws.on('close', () => {
      console.info('WebSocket 连接关闭')
      this.ws = null
      if (this.options.reconnect) {
        this.attemptReconnect()
      }
    })

    this.ws.on('error', (err) => {
      console.error('WebSocket 错误:', JSON.stringify(err))
    })

    this.ws.connect(this.options.url)
  }

  private attemptReconnect(): void {
    if (this.reconnectAttempts >= (this.options.maxReconnectAttempts || 5)) {
      console.error('重连次数已达上限')
      return
    }

    this.reconnectAttempts++
    console.info(`第 ${this.reconnectAttempts} 次重连...`)

    this.reconnectTimer = setTimeout(() => {
      this.connect()
    }, this.options.reconnectInterval)
  }

  send(message: string): void {
    if (this.ws) {
      this.ws.send(message)
    } else {
      console.warn('WebSocket 未连接')
    }
  }

  onMessage(handler: (data: string) => void): void {
    this.messageHandlers.push(handler)
  }

  offMessage(handler: (data: string) => void): void {
    const index = this.messageHandlers.indexOf(handler)
    if (index > -1) {
      this.messageHandlers.splice(index, 1)
    }
  }

  close(): void {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
      this.reconnectTimer = null
    }
    if (this.ws) {
      this.ws.close()
      this.ws = null
    }
  }
}

// 使用示例
const wsManager = new WebSocketManager({
  url: 'wss://api.example.com/ws',
  reconnect: true,
  reconnectInterval: 3000,
  maxReconnectAttempts: 5
})

wsManager.onMessage((data) => {
  console.info('收到消息:', data)
})

wsManager.connect()
wsManager.send(JSON.stringify({ type: 'hello', data: 'world' }))

网络状态监听

API 列表

API说明起始版本
connection.getDefaultNetSync()获取默认网络API 9
connection.getNetCapabilities()获取网络能力信息API 9
connection.getConnectionProperties()获取网络连接属性API 9
connection.on('netAvailable')监听网络可用事件API 9
connection.on('netUnavailable')监听网络不可用事件API 9
connection.on('netCapabilitiesChange')监听网络能力变化事件API 9
connection.off('netAvailable')取消监听网络可用事件API 9

获取当前网络信息

typescript
import { connection } from '@kit.NetworkKit'

function getNetworkInfo(): void {
  try {
    const netHandle = connection.getDefaultNetSync()
    if (netHandle.netId === 0) {
      console.info('当前无网络连接')
      return
    }

    const capabilities = connection.getNetCapabilitiesSync(netHandle)
    console.info('网络类型:', capabilities.bearerTypes)
    console.info('网络能力:', capabilities.networkCap)

    const properties = connection.getConnectionPropertiesSync(netHandle)
    console.info('链路信息:', properties.linkAddress)
  } catch (error) {
    console.error('获取网络信息失败:', JSON.stringify(error))
  }
}

监听网络状态变化

typescript
import { connection } from '@kit.NetworkKit'

class NetworkMonitor {
  private netAvailableHandler: ((data: connection.NetHandle) => void) | null = null
  private netUnavailableHandler: (() => void) | null = null
  private netCapabilitiesChangeHandler: ((data: connection.NetCapabilityInfo) => void) | null = null

  startMonitoring(): void {
    // 监听网络可用
    this.netAvailableHandler = (data) => {
      console.info('网络可用, netId:', data.netId)
    }
    connection.on('netAvailable', this.netAvailableHandler)

    // 监听网络不可用
    this.netUnavailableHandler = () => {
      console.info('网络不可用')
    }
    connection.on('netUnavailable', this.netUnavailableHandler)

    // 监听网络能力变化
    this.netCapabilitiesChangeHandler = (data) => {
      console.info('网络能力变化:', JSON.stringify(data))
    }
    connection.on('netCapabilitiesChange', this.netCapabilitiesChangeHandler)
  }

  stopMonitoring(): void {
    if (this.netAvailableHandler) {
      connection.off('netAvailable', this.netAvailableHandler)
    }
    if (this.netUnavailableHandler) {
      connection.off('netUnavailable', this.netUnavailableHandler)
    }
    if (this.netCapabilitiesChangeHandler) {
      connection.off('netCapabilitiesChange', this.netCapabilitiesChangeHandler)
    }
  }
}

// 使用示例
const monitor = new NetworkMonitor()
monitor.startMonitoring()
// 页面销毁时调用
// monitor.stopMonitoring()

判断网络类型

typescript
import { connection } from '@kit.NetworkKit'

enum NetworkType {
  NONE = 'none',
  WIFI = 'wifi',
  CELLULAR = 'cellular',
  ETHERNET = 'ethernet',
  UNKNOWN = 'unknown'
}

function getNetworkType(): NetworkType {
  try {
    const netHandle = connection.getDefaultNetSync()
    if (netHandle.netId === 0) {
      return NetworkType.NONE
    }

    const capabilities = connection.getNetCapabilitiesSync(netHandle)
    const bearerTypes = capabilities.bearerTypes

    if (bearerTypes.includes(connection.NetBearType.BEARER_WIFI)) {
      return NetworkType.WIFI
    }
    if (bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR)) {
      return NetworkType.CELLULAR
    }
    if (bearerTypes.includes(connection.NetBearType.BEARER_ETHERNET)) {
      return NetworkType.ETHERNET
    }

    return NetworkType.UNKNOWN
  } catch {
    return NetworkType.NONE
  }
}

// 使用示例
const networkType = getNetworkType()
console.info('当前网络类型:', networkType)

错误处理

常见错误码

错误码说明处理建议
401解析参数错误检查请求参数格式
404找不到文件检查文件路径
500未知错误查看系统日志
2300001网络不可用检查网络连接
2300002网络请求超时增加超时时间或检查网络
2300003网络请求失败检查 URL 和网络状态
2300028网络请求被取消用户主动取消

错误处理示例

typescript
import { http } from '@kit.NetworkKit'

interface NetworkError {
  code: number
  message: string
}

async function safeRequest<T>(
  url: string,
  options?: http.HttpRequestOptions
): Promise<T | null> {
  const request = http.createHttp()

  try {
    const response = await request.request(url, {
      connectTimeout: 10000,
      readTimeout: 10000,
      ...options
    })

    if (response.responseCode >= 200 && response.responseCode < 300) {
      return JSON.parse(response.result as string) as T
    } else {
      console.error(`HTTP 错误: ${response.responseCode}`)
      return null
    }
  } catch (error) {
    const err = error as NetworkError
    console.error('请求失败:', err.code, err.message)

    // 根据错误码处理
    switch (err.code) {
      case 2300001:
        // 网络不可用,提示用户检查网络
        break
      case 2300002:
        // 超时,可以重试
        break
      default:
        // 其他错误
        break
    }

    return null
  } finally {
    request.destroy()
  }
}

最佳实践

  1. 始终销毁请求对象:使用完 http.createHttp() 后必须调用 destroy()
  2. 设置超时时间:避免请求挂起,建议设置 10-30 秒
  3. 统一封装:建议封装请求工具类,统一处理错误和 token
  4. 类型安全:为请求和响应定义接口
  5. 错误处理:使用 try/catch 捕获网络异常,根据错误码分类处理
  6. WebSocket 重连:生产环境建议实现自动重连机制
  7. 网络状态监听:在需要时监听网络变化,及时提示用户
  8. 取消请求:页面销毁时取消未完成的请求,避免内存泄漏

参考链接