鸿蒙6.0开发实战:HTTP 网络请求与 API 交互全指南 原创
头像 程序员Feri 2025-11-03 13:04:41    发布
28854 浏览 787 点赞 0 收藏
Hello!我是程序员Feri,专注编程实战技巧分享,深耕AI与机器学习领域多年,致力于把复杂技术拆解成易懂教程,陪大家在AI时代稳步进阶。

在鸿蒙应用开发中,网络请求是连接 “本地应用” 与 “远程服务” 的核心桥梁 —— 无论是拉取后端 API 数据(如用户信息、商品列表)、上传文件,还是调用第三方服务(如天气接口、地图接口),都依赖 HTTP 协议实现。

一、HTTP 协议:鸿蒙网络请求的 “底层规则”

HTTP 作为应用层协议,是鸿蒙网络请求的基础,但在鸿蒙开发中,我们需重点关注其与 “鸿蒙系统特性” 结合的部分 —— 比如协议版本支持、无状态问题的鸿蒙解决方案、安全适配等。

1.1 鸿蒙开发中需关注的 HTTP 核心特性

回顾 HTTP 协议的核心特性,以下 4 点直接影响鸿蒙网络请求的实现:


核心特性鸿蒙开发中的影响与适配
请求 - 响应模式鸿蒙应用作为客户端,需主动发起 GET/POST 等请求;服务器被动响应后,需处理响应体(如 JSON 解析)、状态码(如 404/500 错误)
无状态特性鸿蒙应用需通过 “请求头携带 Token”(如 JWT)或 “Cookie 存储” 实现会话跟踪(鸿蒙推荐用 Token,避免 Cookie 跨设备同步问题)
可扩展性鸿蒙支持自定义请求头(如Authorization: Bearer xxx)、HTTP/2 协议(默认开启,提升传输效率)、HTTPS 加密(强制要求,HTTP 需额外配置)
依赖 TCP鸿蒙的@ohos.net.http模块已封装 TCP 连接管理,开发者无需关心底层连接建立 / 断开,只需关注请求参数与响应处理

1.2 鸿蒙对 HTTP 的特殊适配

  1. 协议版本支持:鸿蒙 4.0 及以上版本默认支持 HTTP/1.1、HTTP/2,部分设备支持 HTTP/3(基于 QUIC),无需额外配置即可享受高性能传输;
  2. 安全限制:鸿蒙默认禁止明文 HTTP 请求(http://),需在module.json5中配置 “网络安全策略” 才能使用;
  3. 权限管控:所有网络请求必须申请INTERNET权限,否则会直接抛出权限异常。

二、前置准备:鸿蒙网络请求的 “准入配置”

在写一行网络请求代码前,必须完成权限配置安全配置(针对 HTTP),这是鸿蒙系统的强制要求,也是新手最易踩坑的点。

2.1 配置网络权限(必做)

鸿蒙应用需在module.json5(模块配置文件)中声明ohos.permission.INTERNET权限,否则无法发起任何网络请求。

配置步骤:

  1. 打开项目的src/main/module.json5文件;
  2. 在module -> reqPermissions数组中添加权限声明:
{  "module": {    "name": "entry",    "type": "entry",    "description": "鸿蒙网络请求示例",    "mainElement": "EntryAbility",    "deviceTypes": ["phone", "tablet"],    // 网络权限配置    "reqPermissions": [      {        "name": "ohos.permission.INTERNET", // 网络权限        "reason": "需要访问网络获取API数据", // 权限申请理由(用户可见)        "usedScene": {          "ability": ["EntryAbility"],          "when": "always" // 始终需要该权限        }      }    ],    // 其他配置...  }}
注意:鸿蒙 6.0 及以上版本中,usedScene.when支持inuse(仅使用时申请),更符合隐私规范,建议优先使用。

2.2 明文 HTTP 请求配置(可选)

鸿蒙默认只允许https://请求(加密传输),若需调用http://接口(如测试环境接口),需额外配置 “网络安全策略”:

配置步骤:

  1. 在src/main/resources/rawfile目录下创建network_security_config.xml文件(若rawfile不存在则新建);
  2. 写入安全配置,允许指定域名的 HTTP 请求:
<?xml version="1.0" encoding="utf-8"?><network-security-config>  <!-- 允许所有HTTP请求(测试环境用,生产环境禁止) -->  <base-config cleartextTrafficPermitted="true" />  <!-- 或仅允许指定域名(更安全) -->  <!-- <domain-config cleartextTrafficPermitted="true">    <domain includeSubdomains="true">api.test.com</domain>  </domain-config> --></network-security-config>
  1. 在module.json5的module -> metaData中引用该配置:
{  "module": {    // 其他配置...    "metaData": {      "customData": [        {          "name": "ohos.application.networkSecurityConfig",          "value": "rawfile/network_security_config.xml"        }      ]    }  }}
警告:生产环境必须使用 HTTPS(https://),禁止配置cleartextTrafficPermitted="true",避免数据泄露风险。

三、核心实战:鸿蒙 HTTP 请求 API 全解析

鸿蒙提供@ohos.net.http模块实现 HTTP 请求,支持 GET(查询)、POST(提交)、PUT(更新)、DELETE(删除)等所有 HTTP 方法。以下从 “基础流程” 到 “实战示例” 逐步讲解,代码可直接在鸿蒙 Page 组件中运行。

3.1 鸿蒙 HTTP 请求的核心流程

无论哪种请求方法,鸿蒙 HTTP 请求都遵循 “5 步流程”,确保资源正确释放(避免内存泄漏):

  1. 导入模块:引入@ohos.net.http的核心类;
  2. 创建请求对象:实例化Http对象,管理单个请求的生命周期;
  3. 配置请求参数:设置 URL、方法、请求头、超时等;
  4. 发送请求并处理响应:获取状态码、响应头、响应体,解析数据(如 JSON);
  5. 销毁请求对象:请求结束后调用destroy(),释放资源。

3.2 实战 1:GET 请求(查询数据)

GET 请求用于 “获取资源”(如拉取列表、详情),参数通常拼接在 URL 后(如https://api.xxx.com/list?page=1&size=10)。

完整代码示例(拉取图片 API):

import http from '@ohos.net.http';import { JsonUtils } from '../utils/JsonUtils'; // 复用前文封装的JSON工具类import { Column, Button, Text, Progress, Toast } from '@ohos.arkui-components';@Entry@Componentstruct HttpGetDemo {  // 状态管理:加载中、请求结果、错误信息  @State isLoading: boolean = false;  @State result: string = '';  @State errorMsg: string = '';  build() {    Column({ space: 20 }) {      Text('HTTP GET请求示例')        .fontSize(18)        .fontWeight(FontWeight.Bold)      // 加载状态显示      if (this.isLoading) {        Progress({ value: 50, total: 100, type: ProgressType.CIRCULAR })        Text('正在拉取数据...')          .fontSize(14)          .fontColor('#666')      }      // 错误信息显示      if (this.errorMsg) {        Text(`错误:${this.errorMsg}`)          .fontSize(14)          .fontColor('#ff4444')          .backgroundColor('#fff3f3')          .padding(10)          .borderRadius(5)          .width('90%')      }      // 结果显示(JSON格式化)      if (this.result && !this.isLoading && !this.errorMsg) {        Text('拉取结果:')          .fontSize(14)          .fontWeight(FontWeight.Bold)          .width('90%')          .textAlign(TextAlign.Start)                Text(this.result)          .fontSize(12)          .backgroundColor('#f5f5f5')          .padding(10)          .borderRadius(5)          .width('90%')          .textAlign(TextAlign.Start)          .whiteSpace(WhiteSpace.PreWrap) // 保留换行      }      // 发起GET请求按钮      Button('发起GET请求(拉取女生图片API)')        .width('90%')        .backgroundColor('#007DFF')        .fontColor('#fff')        .onClick(() => this.sendGetRequest())    }    .height('100%')    .padding(20)    .backgroundColor('#fff')  }  /**   * 发送GET请求:拉取https://api.52vmy.cn/api/img/tu/girl接口数据   */  private async sendGetRequest() {    // 1. 重置状态    this.isLoading = true;    this.errorMsg = '';    this.result = '';    // 2. 创建HTTP请求对象    let httpRequest: http.HttpRequest = http.createHttp();    try {      // 3. 配置请求参数并发送请求      // 注意:GET参数可直接拼接在URL后,或通过params配置(鸿蒙会自动拼接)      const response = await httpRequest.request(        'https://api.52vmy.cn/api/img/tu/girl', // 请求URL        {          method: http.RequestMethod.GET, // 请求方法          header: {            'Content-Type': 'application/json', // 告知服务器响应数据为JSON            'User-Agent': 'HarmonyOS-App/1.0' // 自定义请求头(标识应用)          },          connectTimeout: 5000, // 连接超时:5秒(默认30秒)          readTimeout: 5000 // 读取超时:5秒(默认30秒)          // GET参数可选配置(与URL拼接等效)          // params: {          //   count: 1 // 示例:请求1条数据(需接口支持)          // }        }      );      // 4. 处理响应:先判断状态码      if (response.responseCode === 200) {        // 响应体是ArrayBuffer,需转为字符串        const responseBody = String.fromCharCode.apply(null, new Uint8Array(response.result as ArrayBuffer));        // 用JSON工具类格式化(便于显示)        this.result = JsonUtils.stringify(JSON.parse(responseBody), 2);        Toast.show({ message: 'GET请求成功' });      } else {        // 非200状态码(如404、500)        this.errorMsg = `请求失败,状态码:${response.responseCode}`;      }    } catch (error) {      // 5. 捕获异常(如网络断开、超时)      this.errorMsg = `请求异常:${(error as Error).message}`;    } finally {      // 6. 无论成功/失败,都销毁请求对象(释放资源)      httpRequest.destroy();      this.isLoading = false;    }  }}

3.3 实战 2:POST 请求(提交数据)

POST 请求用于 “提交资源”(如登录、上传表单、创建数据),参数通常放在 “请求体” 中(而非 URL),支持application/json(JSON 格式)、application/x-www-form-urlencoded(表单格式)两种常见类型。

2.1 POST 提交 JSON 格式(最常用)

适用于后端接口要求 “JSON 参数” 的场景(如登录接口):

/** * 发送POST请求(JSON格式参数) * 示例:模拟登录接口(假设接口为https://api.xxx.com/login) */private async sendPostJsonRequest() {  this.isLoading = true;  this.errorMsg = '';  this.result = '';  let httpRequest: http.HttpRequest = http.createHttp();  try {    // 1. 准备JSON参数(登录账号密码)    const loginParams = {      username: 'harmony_dev',      password: '123456' // 实际开发中需加密传输,禁止明文!    };    // 2. 发送POST请求    const response = await httpRequest.request(      'https://api.xxx.com/login', // 模拟登录接口URL      {        method: http.RequestMethod.POST,        header: {          'Content-Type': 'application/json', // 关键:声明参数为JSON格式          'Authorization': 'Basic dGVzdDp0ZXN0' // 若接口需要基础认证,可添加        },        connectTimeout: 5000,        readTimeout: 5000,        // 3. 把JSON参数转为字符串,放入请求体        extraData: JSON.stringify(loginParams)      }    );    // 4. 处理响应    if (response.responseCode === 200) {      const responseBody = String.fromCharCode.apply(null, new Uint8Array(response.result as ArrayBuffer));      const responseJson = JSON.parse(responseBody);      // 示例:提取Token(登录成功后后端返回)      const token = responseJson.data.token;      // 存储Token(后续请求携带,实现会话跟踪)      // PreferencesUtil.put('user_token', token); // 可封装Preferences工具类      this.result = JsonUtils.stringify(responseJson, 2);      Toast.show({ message: `登录成功,Token:${token.substring(0, 10)}...` });    } else {      this.errorMsg = `登录失败,状态码:${response.responseCode}`;    }  } catch (error) {    this.errorMsg = `登录异常:${(error as Error).message}`;  } finally {    httpRequest.destroy();    this.isLoading = false;  }}

2.2 POST 提交表单格式

适用于后端接口要求 “表单参数” 的场景(如传统 Web 接口):

/** * 发送POST请求(表单格式参数) * 示例:模拟用户注册接口 */private async sendPostFormRequest() {  this.isLoading = true;  this.errorMsg = '';  this.result = '';  let httpRequest: http.HttpRequest = http.createHttp();  try {    // 1. 准备表单参数(转为URL编码格式)    const formParams = new URLSearchParams();    formParams.append('username', 'new_harmony_user');    formParams.append('email', 'user@example.com');    formParams.append('password', 'encrypted_pwd'); // 实际开发需加密    // 2. 发送POST请求    const response = await httpRequest.request(      'https://api.xxx.com/register', // 模拟注册接口URL      {        method: http.RequestMethod.POST,        header: {          // 关键:声明参数为表单格式          'Content-Type': 'application/x-www-form-urlencoded',        },        connectTimeout: 5000,        readTimeout: 5000,        // 3. 表单参数转为字符串,放入请求体        extraData: formParams.toString()      }    );    // 4. 处理响应    if (response.responseCode === 200) {      const responseBody = String.fromCharCode.apply(null, new Uint8Array(response.result as ArrayBuffer));      this.result = JsonUtils.stringify(JSON.parse(responseBody), 2);      Toast.show({ message: '注册成功' });    } else {      this.errorMsg = `注册失败,状态码:${response.responseCode}`;    }  } catch (error) {    this.errorMsg = `注册异常:${(error as Error).message}`;  } finally {    httpRequest.destroy();    this.isLoading = false;  }}

四、鸿蒙网络请求的 “避坑指南” 与高级特性

4.1 常见错误与解决方案


错误场景原因解决方案
权限异常(permission denied)未配置INTERNET权限在module.json5的reqPermissions中添加权限
HTTP 请求被拒绝(cleartext not permitted)鸿蒙禁止明文 HTTP 请求1. 改用 HTTPS;2. 配置network_security_config.xml
超时异常(timeout)网络差、接口响应慢、超时时间设置过短1. 延长connectTimeout/readTimeout(建议 5-10 秒);2. 增加网络状态判断
响应体解析失败(JSON.parse error)响应体不是 JSON 格式、接口返回错误信息(如 HTML)1. 先打印responseBody确认格式;2. 增加解析异常捕获
跨域问题(CORS error)前端直接调用非同源接口(浏览器环境常见,鸿蒙应用端少见)1. 后端配置 CORS 头(Access-Control-Allow-Origin);2. 通过后端代理转发请求

4.2 高级特性:网络状态监听

在发起请求前,先判断设备是否联网,可避免无效请求(提升用户体验)。鸿蒙提供@ohos.net.connection模块监听网络状态:

import connection from '@ohos.net.connection';/** * 检查设备是否联网 * @returns true:联网;false:未联网 */private async checkNetworkStatus(): Promise<boolean> {  try {    // 获取当前网络状态    const netStatus = await connection.getActiveNetworkInfo();    // 网络类型:1=无网络,2=移动网络,3=WiFi    return netStatus.type !== connection.NetworkType.NETWORK_TYPE_NONE;  } catch (error) {    console.error('检查网络状态失败:', error);    return false;  }}// 在请求前调用private async sendGetRequest() {  // 先检查网络  const isOnline = await this.checkNetworkStatus();  if (!isOnline) {    this.errorMsg = '当前无网络,请检查网络设置';    Toast.show({ message: '无网络连接' });    return;  }  // 后续请求逻辑...}

4.3 高级特性:请求取消

若用户在请求过程中退出页面,需取消未完成的请求,避免内存泄漏或无效回调。鸿蒙的HttpRequest支持abort()方法:

// 1. 定义请求对象(改为类成员变量,便于外部访问)private httpRequest: http.HttpRequest | null = null;// 2. 发起请求时赋值private async sendGetRequest() {  this.httpRequest = http.createHttp(); // 赋值给类成员  try {    const response = await this.httpRequest.request(/* 参数省略 */);    // ...处理响应  } catch (error) {    // 捕获取消请求的异常(abort会触发error)    if ((error as Error).message.includes('abort')) {      console.log('请求已取消');      this.errorMsg = '请求已取消';    } else {      this.errorMsg = `请求异常:${(error as Error).message}`;    }  }}// 3. 页面销毁时取消请求(如退出Page组件)aboutToDisappear() {  if (this.httpRequest) {    this.httpRequest.abort(); // 取消请求    this.httpRequest.destroy(); // 销毁对象    this.httpRequest = null;  }}

五、最佳实践:封装鸿蒙网络工具类

实际开发中,若每个组件都写重复的请求逻辑(创建对象、配置头、错误处理),会导致代码冗余。建议封装一个HttpUtil工具类,统一管理网络请求。

5.1 封装HttpUtil.ets

// utils/HttpUtil.etsimport http from '@ohos.net.http';import connection from '@ohos.net.connection';import { PreferencesUtil } from './PreferencesUtil'; // 封装的Preferences工具类(用于存Token)/** * 鸿蒙HTTP网络工具类:统一请求配置、错误处理、Token携带 */export class HttpUtil {  // 基础URL(不同环境可切换,如开发/测试/生产)  private static BASE_URL = 'https://api.52vmy.cn';  /**   * 统一发送HTTP请求   * @param url 接口路径(如'/api/img/tu/girl')   * @param method 请求方法(GET/POST/PUT/DELETE)   * @param params 请求参数(GET:拼接URL;POST:放入请求体)   * @param header 自定义请求头(会覆盖默认头)   * @returns 解析后的JSON响应体   */  static async request<T>(    url: string,    method: http.RequestMethod = http.RequestMethod.GET,    params?: Record<string, any>,    header?: Record<string, string>  ): Promise<T> {    // 1. 检查网络状态    const isOnline = await this.checkNetwork();    if (!isOnline) {      throw new Error('当前无网络,请检查网络设置');    }    // 2. 创建请求对象    const httpRequest = http.createHttp();    // 完整URL(基础URL + 接口路径)    const fullUrl = this.BASE_URL + url;    try {      // 3. 配置默认请求头(所有请求共享)      const defaultHeader: Record<string, string> = {        'Content-Type': 'application/json',        'User-Agent': 'HarmonyOS-App/1.0',        // 自动携带Token(登录后存储,未登录则不携带)        'Authorization': await this.getTokenHeader()      };      // 4. 合并自定义请求头(自定义会覆盖默认)      const finalHeader = { ...defaultHeader, ...header };      // 5. 处理请求参数      let requestConfig: http.HttpRequestOptions = {        method,        header: finalHeader,        connectTimeout: 8000,        readTimeout: 8000      };      // 处理GET参数(拼接URL)      if (method === http.RequestMethod.GET && params) {        const queryStr = new URLSearchParams(params).toString();        requestConfig.url = queryStr ? `${fullUrl}?${queryStr}` : fullUrl;      }      // 处理POST/PUT参数(放入请求体)      if ([http.RequestMethod.POST, http.RequestMethod.PUT].includes(method) && params) {        requestConfig.url = fullUrl;        // 根据Content-Type判断参数格式        if (finalHeader['Content-Type'] === 'application/x-www-form-urlencoded') {          requestConfig.extraData = new URLSearchParams(params).toString();        } else {          requestConfig.extraData = JSON.stringify(params);        }      }      // 6. 发送请求      const response = await httpRequest.request(requestConfig.url || fullUrl, requestConfig);      // 7. 处理响应状态码      if (response.responseCode < 200 || response.responseCode >= 300) {        throw new Error(`请求失败,状态码:${response.responseCode}`);      }      // 8. 解析响应体(ArrayBuffer → 字符串 → JSON)      const responseBody = String.fromCharCode.apply(null, new Uint8Array(response.result as ArrayBuffer));      const responseJson = JSON.parse(responseBody) as T;      return responseJson;    } catch (error) {      // 统一错误处理(可添加日志上报)      console.error(`HTTP请求失败(${fullUrl}):`, error);      throw error; // 抛出错误,让调用方处理UI反馈    } finally {      // 9. 销毁请求对象      httpRequest.destroy();    }  }  /**   * 检查网络状态   */  private static async checkNetwork(): Promise<boolean> {    try {      const netStatus = await connection.getActiveNetworkInfo();      return netStatus.type !== connection.NetworkType.NETWORK_TYPE_NONE;    } catch (error) {      console.error('检查网络状态失败:', error);      return false;    }  }  /**   * 获取Token请求头(格式:Bearer xxx)   */  private static async getTokenHeader(): Promise<string> {    try {      // 从Preferences中获取存储的Token      const token = await PreferencesUtil.get('user_token', '');      return token ? `Bearer ${token}` : '';    } catch (error) {      console.error('获取Token失败:', error);      return '';    }  }  // 封装常用请求方法(简化调用)  static async get<T>(url: string, params?: Record<string, any>, header?: Record<string, string>): Promise<T> {    return this.request<T>(url, http.RequestMethod.GET, params, header);  }  static async post<T>(url: string, params?: Record<string, any>, header?: Record<string, string>): Promise<T> {    return this.request<T>(url, http.RequestMethod.POST, params, header);  }  static async put<T>(url: string, params?: Record<string, any>, header?: Record<string, string>): Promise<T> {    return this.request<T>(url, http.RequestMethod.PUT, params, header);  }  static async delete<T>(url: string, params?: Record<string, any>, header?: Record<string, string>): Promise<T> {    return this.request<T>(url, http.RequestMethod.DELETE, params, header);  }}

5.2 工具类使用示例

// 组件中调用(简化90%代码)private async useHttpUtil() {  this.isLoading = true;  try {    // 1. 调用GET接口(拉取图片API)    const getResult = await HttpUtil.get('/api/img/tu/girl', { count: 1 });    this.result = JsonUtils.stringify(getResult, 2);    // 2. 调用POST接口(模拟登录)    // const loginResult = await HttpUtil.post('/login', {    //   username: 'harmony_dev',    //   password: 'encrypted_pwd'    // });    // await PreferencesUtil.put('user_token', loginResult.data.token); // 存储Token    Toast.show({ message: '请求成功' });  } catch (error) {    this.errorMsg = (error as Error).message;    Toast.show({ message: `请求失败:${this.errorMsg}` });  } finally {    this.isLoading = false;  }}

六、总结

鸿蒙 HTTP 网络请求的核心是 “@ohos.net.http模块 + 前置配置 + 统一封装”:

  1. 前置配置是基础:必须申请INTERNET权限,HTTPS 优先,HTTP 需额外配置安全策略;
  2. 核心流程要牢记:创建请求对象 → 配置参数 → 发送请求 → 处理响应 → 销毁对象,避免内存泄漏;
  3. 错误处理不可少:网络状态、超时、状态码、解析异常需全面覆盖;
  4. 封装工具类是关键:统一管理基础 URL、请求头、Token,减少重复代码,提升维护效率。



©本站发布的所有内容,包括但不限于文字、图片、音频、视频、图表、标志、标识、广告、商标、商号、域名、软件、程序等,除特别标明外,均来源于网络或用户投稿,版权归原作者或原出处所有。我们致力于保护原作者版权,若涉及版权问题,请及时联系我们进行处理。
分类
HarmonyOS

暂无评论数据

发布

头像

程序员Feri

13 年编程老炮,华为开发者专家,北科大硕士,实战派技术人(开发/架构/教学/创业),拆解编程技巧、分享副业心得,记录程序员的进阶路,AI 时代一起稳稳向前。

19

帖子

0

提问

206

粉丝

关注
热门推荐
地址:北京市朝阳区北三环东路三元桥曙光西里甲1号第三置业A座1508室 商务内容合作QQ:2291221 电话:13391790444或(010)62178877
版权所有:电脑商情信息服务集团 北京赢邦策略咨询有限责任公司
声明:本媒体部分图片、文章来源于网络,版权归原作者所有,我司致力于保护作者版权,如有侵权,请与我司联系删除
京ICP备:2022009079号-2
京公网安备:11010502051901号
ICP证:京B2-20230255