第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提交任务、发送数据(文生图、图生视频任务创建)createSeedreamImage、createImg2VideoTask
http.RequestMethod.GET查询任务状态、下载文件querySeedanceTask、downloadVideo

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-Type 为 application/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 对象包含两个核心字段:

字段类型说明
responseCodenumberHTTP 状态码(200、400、500 等)
resultstring | 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 秒
});

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

const videoBuffer = response.result as ArrayBuffer;

7.2 保存到本地文件

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

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.openSync → writeSync → closeSync

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 超时配置对照表

参数推荐值配置位置
connectTimeout30000(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开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐