第3.1篇:HTTP 网络请求——@kit.NetworkKit 全解析

系列:鸿蒙 AI 服务与网络集成篇
难度:⭐⭐ 进阶
前置知识:1.2 ArkUI 声明式 UI 基础
涉及源文件products/default/src/main/ets/services/AIGenerationService.ets


在这里插入图片描述

在"画伴梦工厂"中,所有 AI 能力的调用——文生图、图生视频、任务状态查询、视频文件下载——都离不开网络请求。HarmonyOS 提供了 @kit.NetworkKit 作为标准的 HTTP 网络通信解决方案,它封装了从会话创建、请求配置、响应处理到资源释放的完整链路。

本文将以 AIGenerationService.ets 中的真实代码为线索,全面解析 @kit.NetworkKit 的核心用法。


一、@kit.NetworkKit 概述

@kit.NetworkKit 是 HarmonyOS 提供的原生网络通信 Kit,其核心模块是 http 模块。它提供了基于 HTTP/HTTPS 协议的网络请求能力,支持:

  • 会话管理:通过 http.createHttp() 创建独立的 HTTP 会话
  • 多种请求方法:GET、POST、PUT、DELETE 等
  • 灵活的请求配置:自定义 Header、请求体、超时时间、期望响应类型
  • 完整的响应信息:状态码、响应头、响应体
  • 资源生命周期管理:显式销毁会话以释放 native 资源

在项目中引入方式十分简洁:

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

@kit.NetworkKit 是鸿蒙的元能力 Kit,无需在 oh-package.json5 中额外声明依赖,开箱即用。


二、http.createHttp——创建 HTTP 会话

http.createHttp() 是使用 @kit.NetworkKit 的入口。它创建一个 HttpRequest 对象,代表一个独立的 HTTP 会话(Session)。

基本用法

const request = http.createHttp();

每次调用 createHttp 都会返回一个新会话实例。这个会话可以被多次复用来发起请求,不需要每次请求都重新创建。

会话的作用域

AIGenerationService.ets 中,每个需要网络请求的私有方法都会在方法内部创建自己的会话:

private static async createSeedreamImage(prompt: string): Promise<string> {
  const request = http.createHttp();   // 创建会话
  try {
    // ... 发起请求
  } finally {
    request.destroy();                 // 销毁会话
  }
}

这种"方法内创建、销毁"的模式是最安全的使用方式,确保每个会话的生命周期可控,避免资源泄漏。


三、Request 配置——method、header、extraData

http.HttpRequestOptions 是请求配置的核心接口。来看项目中一个典型的 POST 请求配置:

const options: http.HttpRequestOptions = {
  method: http.RequestMethod.POST,
  expectDataType: http.HttpDataType.STRING,
  connectTimeout: 30000,
  readTimeout: 90000,
  header: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + ARK_API_KEY
  },
  extraData: JSON.stringify(requestBody)
};
const response = await request.request(ARK_IMAGE_API_URL, options);

3.1 method——请求方法

通过 http.RequestMethod 枚举指定 HTTP 方法。项目中主要使用了两种:

方法 使用场景 示例
http.RequestMethod.POST 提交任务、发送数据(文生图、图生视频任务创建) createSeedreamImagecreateImg2VideoTask
http.RequestMethod.GET 查询任务状态、下载文件 querySeedanceTaskdownloadVideo

3.2 header——请求头

请求头通过普通 JavaScript 对象配置。项目中根据接口需求定义了两种 Header 类型:

interface ArkHeaders {
  'Content-Type': string;
  'Authorization': string;
}

interface JsonHeaders {
  'Content-Type': string;
}

ArkHeaders(用于火山引擎 Ark API):

const headers: ArkHeaders = {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer ' + ARK_API_KEY
};
  • Content-Type: application/json 声明请求体为 JSON 格式
  • Authorization: Bearer <token> 携带 API 密钥进行身份认证

JsonHeaders(用于自建中间件服务):

const headers: JsonHeaders = {
  'Content-Type': 'application/json'
};

自建中间件不需要 API 密钥,因此仅需声明内容类型。

3.3 extraData——请求体

extraData 用于传递请求体数据。需要注意:extraData 的类型必须与 header 中的 Content-Type 保持一致

Content-Typeapplication/json 时,extraData 必须是已经序列化的 JSON 字符串

// 先构造请求体对象
const body: ImageGenerationRequest = {
  model: 'doubao-seedream-4-0-250828',
  prompt: prompt,
  response_format: 'url',
  size: '1024x1024',
  guidance_scale: 2.5,
  watermark: true
};

// 序列化为 JSON 字符串后传入 extraData
extraData: JSON.stringify(body)

这是一个常见的易错点。许多初学者会直接将 JavaScript 对象传入 extraData,但 HarmonyOS 的 HttpRequest 要求开发者自行完成序列化。这也是项目中总是先写 JSON.stringify() 再传入的原因。


四、超时配置——ConnectTimeout / ReadTimeout

网络请求的两个核心超时参数,在项目中针对不同场景做了精细的差异化配置:

ConnectTimeout(连接超时)

指从发起请求到建立 TCP 连接的最长等待时间。项目中对所有请求统一设置为 30000ms(30 秒)

connectTimeout: 30000

30 秒是一个合理的默认值——既不会让用户在弱网环境下等待过久,也不会因为过短而在正常慢速网络上频繁失败。

ReadTimeout(读取超时)

指建立连接后等待服务器返回数据的最大时间。不同接口的耗时差异很大,项目针对不同场景配置了不同的读取超时:

场景 超时时间 常量 说明
文生图请求 90 秒 90000 图片生成通常较快
图生视频任务创建 180 秒 SEEDANCE_CREATE_TIMEOUT_MS 首次提交可能稍慢
任务状态查询 240 秒 SEEDANCE_QUERY_TIMEOUT_MS 轮询查询等待结果
视频文件下载 240 秒 SEEDANCE_DOWNLOAD_TIMEOUT_MS 大文件下载需要更长等待
// 短请求——文生图
readTimeout: 90000

// 长请求——视频下载
readTimeout: SEEDANCE_DOWNLOAD_TIMEOUT_MS  // 240000ms

超时设计原则:ConnectTimeout 可以统一,但 ReadTimeout 必须根据接口的实际耗时特点差异化配置。AI 推理类接口通常需要较长的等待时间,因此项目中大多数 ReadTimeout 设置在 90 秒以上。


五、响应处理——responseCode 与 result

请求返回的 http.HttpResponse 对象包含两个核心字段:

字段 类型 说明
responseCode number HTTP 状态码(200、400、500 等)
result string | object | ArrayBuffer 响应体内容,类型由 expectDataType 决定

5.1 状态码校验

项目中对所有 HTTP 响应都进行了严格的状态码校验:

if (response.responseCode < 200 || response.responseCode >= 300) {
  throw new Error('图片生成任务创建失败:' + response.responseCode.toString());
}

这段代码使用了范围判断而非简单的 !== 200,好处是:

  • 自动识别所有 2xx 成功状态码(200、201、204 等),兼容性更好
  • 将 3xx 重定向、4xx 客户端错误、5xx 服务端错误统一归为失败

5.2 响应体解析

response.result 的类型由请求配置中的 expectDataType 决定。当设为 http.HttpDataType.STRING 时,result 为字符串:

const responseText = response.result.toString();
const responseBody = JSON.parse(responseText) as ImageGenerationResponse;

重要:即使 expectDataType 已指定为 STRING,代码中仍然调用了 .toString()。这是一种防御性编程习惯,确保即使底层行为发生变化,也能获得字符串以供 JSON.parse 使用。

5.3 业务错误处理

HTTP 状态码正常(2xx)不代表业务成功。项目在状态码校验之后,还会检查响应体中的业务错误字段:

// 检查 API 返回的业务错误
if (responseBody.error && responseBody.error.message) {
  throw new Error(responseBody.error.message);
}

// 检查数据完整性
if (!responseBody.data || responseBody.data.length === 0) {
  throw new Error('图片生成接口未返回图片');
}

这种双层校验模式——先校验 HTTP 协议层(responseCode),再校验业务逻辑层(response body)——是可靠网络通信的基础。


六、Session 复用与销毁

6.1 为什么必须销毁

http.createHttp() 创建的会话在 native 层持有资源(socket 连接、内存缓冲区等)。如果不主动销毁,可能会导致资源泄漏。项目中的所有 HTTP 请求都遵循了 try-finally-destroy 模式:

private static async createSeedreamImage(prompt: string): Promise<string> {
  const request = http.createHttp();
  try {
    // ... 请求处理
    return imageData.url;
  } finally {
    request.destroy();  // 无论成功或异常,都确保销毁
  }
}

finally 块确保无论请求成功还是抛出异常destroy() 都会被执行。这是防止资源泄漏的最可靠模式。

6.2 长时间轮询中的会话复用

pollImg2VideoTask 方法中,项目采用了一个不同的策略——每次轮询都创建新的会话:

private static async queryImg2VideoTask(taskId: string): Promise<Img2VideoStatusResponse> {
  const request = http.createHttp();
  try {
    // ... 发起单个查询请求
  } finally {
    request.destroy();
  }
}

轮询循环(每 5 秒一次)并不直接创建会话,而是调用 queryImg2VideoTask 这个独立方法,由该方法自行创建和销毁会话。这种设计的好处是:

  1. 避免连接泄漏——即使某次轮询异常中断,该次会话已被 finally 清理
  2. 避免连接过期——长时间复用的连接可能被服务端断开,每次新建更可靠
  3. 代码职责清晰——每个方法只负责自己的会话生命周期

6.3 何时可以复用

会话复用的典型场景是短时间内连续发送多个请求到同一服务器。例如:

const request = http.createHttp();
try {
  const r1 = await request.request(urlA, optionsA);
  const r2 = await request.request(urlB, optionsB);
  const r3 = await request.request(urlC, optionsC);
} finally {
  request.destroy();
}

项目中没有采用这种模式,而是选择了"每次请求独立会话"的方式。对于与不同 API 服务(Ark 火山引擎、自建中间件)交互的场景,这种方式更加安全可靠。


七、ARRAY_BUFFER 响应类型与文件下载

对于图片和视频等二进制文件的下载,需要将 expectDataType 设置为 http.HttpDataType.ARRAY_BUFFER

7.1 下载配置

const response = await request.request(remoteUrl, {
  method: http.RequestMethod.GET,
  expectDataType: http.HttpDataType.ARRAY_BUFFER,
  connectTimeout: 30000,
  readTimeout: SEEDANCE_DOWNLOAD_TIMEOUT_MS  // 240 秒
});

expectDataTypeARRAY_BUFFER 时,response.result 的类型变为 ArrayBuffer,需要通过 as 断言获取:

const videoBuffer = response.result as ArrayBuffer;

7.2 保存到本地文件

获取到 ArrayBuffer 后,配合 @kit.CoreFileKitfileIo 模块即可写入本地文件:

const context = getContext() as common.UIAbilityContext;
const path = context.filesDir + '/seedance_' + taskId + '.mp4';
const file = fileIo.openSync(path, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
try {
  fileIo.writeSync(file.fd, videoBuffer);
} finally {
  fileIo.closeSync(file);
}
return 'file://' + path;

在这个过程中,http 模块只负责"下载到内存",fileIo 模块负责"从内存写入磁盘"。两者分工明确:

模块 职责 对应方法
@kit.NetworkKit HTTP 从网络接收二进制数据 request() + ARRAY_BUFFER
@kit.CoreFileKit fileIo 将内存数据写入文件 fileIo.openSyncwriteSynccloseSync

7.3 图片下载的复用

项目中 downloadImageAsArrayBuffer 方法也同样使用了 ARRAY_BUFFER 模式来下载远程图片:

private static async downloadImageAsArrayBuffer(url: string): Promise<ArrayBuffer> {
  const request = http.createHttp();
  try {
    const response = await request.request(url, {
      method: http.RequestMethod.GET,
      expectDataType: http.HttpDataType.ARRAY_BUFFER,
      connectTimeout: 30000,
      readTimeout: 90000
    });
    if (response.responseCode < 200 || response.responseCode >= 300) {
      throw new Error('图片下载失败:' + response.responseCode.toString());
    }
    return response.result as ArrayBuffer;
  } finally {
    request.destroy();
  }
}

注意这里将 ArrayBuffer 直接作为方法返回值,由调用方决定如何处理(压缩、编码、保存等),体现了职责分离的设计思想。


八、最佳实践总结

8.1 请求模式对比

模式 优点 缺点 适用场景
单会话单请求(项目采用) 生命周期清晰、无泄漏风险 每次请求有创建开销 与多个不同 API 服务交互
单会话多请求 连接复用、性能更优 需自行管理生命周期 连续请求同一服务器

8.2 超时配置对照表

参数 推荐值 配置位置
connectTimeout 30000(30 秒) http.HttpRequestOptions
readTimeout(普通 API) 90000(90 秒) http.HttpRequestOptions
readTimeout(AI 推理) 180000240000(34 分钟) http.HttpRequestOptions

8.3 安全编码清单

  1. always destroy:无论成功还是异常,finally 块中执行 request.destroy()
  2. 序列化先行extraData 接收字符串,使用 JSON.stringify 序列化对象
  3. 状态码范围校验:用 responseCode < 200 || responseCode >= 300 而非 !== 200
  4. 业务错误二次校验:HTTP 2xx 不等于业务成功,需解析响应体中的 error 字段
  5. 二进制文件用 ARRAY_BUFFER:下载图片或视频时显式指定 expectDataType: http.HttpDataType.ARRAY_BUFFER
  6. ConnectTimeout 统一,ReadTimeout 差异化:根据接口耗时特征分别配置

8.4 完整流程时序

以一次文生图调用为例,展示完整的 HTTP 请求生命周期:

调用方(generateImage)
    │
    ▼
createSeedreamImage(prompt)
    │
    ├── request = http.createHttp()       创建会话
    │
    ├── request.request(url, options)     发起 POST 请求
    │     ├── connectTimeout: 30000       等待连接建立
    │     ├── readTimeout: 90000          等待响应返回
    │     └── extraData: JSON.stringify    请求体序列化
    │
    ├── response.responseCode 校验         检查 HTTP 状态码
    ├── responseBody.error 校验            检查业务错误
    ├── response.data[0] 校验              检查数据完整性
    │
    └── finally → request.destroy()       销毁会话

总结

本文通过"画伴梦工厂"中 AIGenerationService.ets 的真实代码,全面解析了 @kit.NetworkKit 的 HTTP 网络请求能力:

知识点 实现方式
创建 HTTP 会话 http.createHttp()
POST 请求配置 method + header + extraData + JSON.stringify
GET 请求配置 method: http.RequestMethod.GET + expectDataType
超时控制 connectTimeout + readTimeout 差异化配置
响应处理 responseCode 范围校验 + 业务错误二次校验
二进制下载 expectDataType: ARRAY_BUFFER + fileIo.writeSync
资源释放 try-finally 模式中的 request.destroy()

下一篇: 第 3.2 篇将深入 火山引擎 Seedream 文生图 API 对接,基于本文的 HTTP 请求基础,拆解请求体组装、响应解析和错误处理的完整实战。


参考源码

本文所有代码均来自项目文件:

  • products/default/src/main/ets/services/AIGenerationService.ets — AI 服务核心,包含完整的 HTTP 请求封装、多种超时策略、ARRAY_BUFFER 下载、以及 try-finally-destroy 资源管理模式
Logo

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

更多推荐