从零开始掌握HarmonyOS开发HTTP网络编程:@ohos.net.http全解
从零开始掌握鸿蒙HTTP网络编程,让你的应用与世界互联
一、背景与动机:为什么HTTP请求如此重要?
说真的,现在的应用如果离了网络,基本就废了一大半功能。你想想看,新闻App得从服务器拉文章,电商App得获取商品列表,社交App得收发消息——这些背后全都是HTTP请求在默默工作。
鸿蒙系统提供了@ohos.net.http模块,这是官方推荐的网络请求方案。相比传统的WebView或者第三方库,它有几个明显优势:
原生性能优势:直接调用系统底层网络栈,延迟更低,内存占用更小。就像开车走专用道,比挤公交快多了。
API设计友好:支持Promise异步调用,配合ArkTS的async/await语法,代码写起来像同步逻辑一样清晰。再也不用陷入回调地狱了。
安全可控:系统级权限管理,支持HTTPS证书校验,企业级应用也不用担心数据泄露问题。
鸿蒙生态集成:自动适配不同设备的网络环境,手机、平板、车机、智能手表都能用同一套代码。
二、核心原理:HTTP请求在鸿蒙中如何工作?
先理解一个概念:HTTP请求的生命周期。这就像寄快递的过程——填单子、打包、发货、运输、签收、拆包。
2.1 核心类与接口
鸿蒙HTTP模块主要包含这几个关键角色:
| 类/接口 | 作用 | 使用场景 |
|---|---|---|
http.createHttp() |
创建HTTP请求对象 | 每次请求前必须调用 |
HttpRequest |
请求对象实例 | 配置参数、发送请求 |
HttpResponse |
响应对象 | 获取状态码、响应头、响应体 |
HttpMethod |
HTTP方法枚举 | GET/POST/PUT/DELETE等 |
2.2 请求流程详解
第一步:创建请求对象
import http from '@ohos.net.http';
// 每个请求都需要独立的对象
let httpRequest = http.createHttp();
第二步:配置并发送请求
// 支持Promise,可以用await
let response = await httpRequest.request(
'https://api.example.com/data', // 请求URL
{
method: http.RequestMethod.GET, // 请求方法
header: { 'Content-Type': 'application/json' }, // 请求头
connectTimeout: 60000, // 连接超时60秒
readTimeout: 60000 // 读取超时60秒
}
);
第三步:处理响应
if (response.responseCode === 200) {
let result = JSON.parse(response.result as string);
console.info('数据获取成功:', result);
}
第四步:销毁对象(重要!)
httpRequest.destroy(); // 释放资源,防止内存泄漏
三、代码实战:三种典型场景
场景一:GET请求获取数据列表
这是最常见的用法——从服务器拉取数据。比如获取新闻列表、商品目录、用户信息等。
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
// 新闻列表数据模型
interface NewsItem {
id: number;
title: string;
author: string;
publishTime: string;
coverImage: string;
}
interface NewsResponse {
code: number;
message: string;
data: NewsItem[];
}
// GET请求示例:获取新闻列表
async function fetchNewsList(page: number = 1, pageSize: number = 20): Promise<NewsItem[]> {
let httpRequest = http.createHttp();
try {
// 构建带查询参数的URL
const url = `https://api.news-app.com/v1/articles?page=${page}&size=${pageSize}`;
// 发起GET请求
let response = await httpRequest.request(url, {
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your_token_here', // 认证token
'User-Agent': 'HarmonyOS-NewsApp/1.0'
},
connectTimeout: 30000, // 连接超时30秒
readTimeout: 30000 // 读取超时30秒
});
// 检查响应状态码
if (response.responseCode === http.ResponseCode.OK) {
// 解析JSON响应
let result: NewsResponse = JSON.parse(response.result as string);
if (result.code === 0) {
console.info(`成功获取${result.data.length}条新闻`);
return result.data;
} else {
console.error(`业务错误: ${result.message}`);
return [];
}
} else {
console.error(`HTTP错误: ${response.responseCode}`);
return [];
}
} catch (error) {
let e = error as BusinessError;
console.error(`请求异常: ${e.code} - ${e.message}`);
return [];
} finally {
// 无论成功失败,都要销毁请求对象
httpRequest.destroy();
}
}
// 在UI组件中使用
@Entry
@Component
struct NewsListPage {
@State newsList: NewsItem[] = [];
@State isLoading: boolean = false;
async aboutToAppear() {
this.isLoading = true;
this.newsList = await fetchNewsList(1, 20);
this.isLoading = false;
}
build() {
Column() {
if (this.isLoading) {
LoadingProgress()
.width(50)
.height(50)
} else {
List() {
ForEach(this.newsList, (item: NewsItem) => {
ListItem() {
this.NewsCard(item)
}
}, (item: NewsItem) => item.id.toString())
}
.width('100%')
.layoutWeight(1)
}
}
}
@Builder
NewsCard(item: NewsItem) {
Row() {
Image(item.coverImage)
.width(100)
.height(80)
.objectFit(ImageFit.Cover)
Column() {
Text(item.title)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.maxLines(2)
Text(`${item.author} · ${item.publishTime}`)
.fontSize(12)
.fontColor('#999')
}
.layoutWeight(1)
.margin({ left: 10 })
}
.padding(10)
}
}
场景二:POST请求提交表单数据
用户登录、注册、发布内容等场景,需要向服务器提交数据。
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
// 用户登录请求参数
interface LoginRequest {
username: string;
password: string;
deviceId?: string; // 设备标识,可选
}
// 登录响应数据
interface LoginResponse {
code: number;
message: string;
data: {
userId: string;
token: string;
refreshToken: string;
expiresIn: number;
};
}
// POST请求示例:用户登录
async function userLogin(credentials: LoginRequest): Promise<string | null> {
let httpRequest = http.createHttp();
try {
// 添加设备信息
let requestBody = {
...credentials,
deviceId: 'HarmonyOS-Device-001',
platform: 'HarmonyOS',
timestamp: Date.now()
};
let response = await httpRequest.request(
'https://api.example.com/v1/auth/login',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
// POST请求需要序列化请求体
extraData: JSON.stringify(requestBody),
connectTimeout: 15000,
readTimeout: 15000
}
);
if (response.responseCode === http.ResponseCode.OK) {
let result: LoginResponse = JSON.parse(response.result as string);
if (result.code === 0 && result.data) {
// 保存token到本地存储
console.info('登录成功,用户ID:', result.data.userId);
return result.data.token;
} else {
console.error('登录失败:', result.message);
return null;
}
} else if (response.responseCode === http.ResponseCode.UNAUTHORIZED) {
console.error('认证失败:用户名或密码错误');
return null;
} else {
console.error('服务器错误:', response.responseCode);
return null;
}
} catch (error) {
let e = error as BusinessError;
console.error('网络异常:', e.message);
return null;
} finally {
httpRequest.destroy();
}
}
// 登录页面组件
@Entry
@Component
struct LoginPage {
@State username: string = '';
@State password: string = '';
@State isLogging: boolean = false;
@State errorMsg: string = '';
async handleLogin() {
if (!this.username || !this.password) {
this.errorMsg = '请输入用户名和密码';
return;
}
this.isLogging = true;
this.errorMsg = '';
let token = await userLogin({
username: this.username,
password: this.password
});
this.isLogging = false;
if (token) {
// 登录成功,跳转到主页
console.info('准备跳转主页');
} else {
this.errorMsg = '登录失败,请重试';
}
}
build() {
Column() {
Text('用户登录')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 30 })
TextInput({ placeholder: '用户名' })
.width('80%')
.height(50)
.onChange((value) => this.username = value)
TextInput({ placeholder: '密码' })
.width('80%')
.height(50)
.type(InputType.Password)
.margin({ top: 15 })
.onChange((value) => this.password = value)
if (this.errorMsg) {
Text(this.errorMsg)
.fontColor('#D0021B')
.fontSize(14)
.margin({ top: 10 })
}
Button(this.isLogging ? '登录中...' : '登录')
.width('80%')
.height(50)
.margin({ top: 30 })
.enabled(!this.isLogging)
.onClick(() => this.handleLogin())
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
场景三:文件上传(multipart/form-data)
上传图片、视频等文件,需要使用multipart格式。
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
import fs from '@ohos.file.fs';
// 文件上传响应
interface UploadResponse {
code: number;
message: string;
data: {
fileId: string;
url: string;
size: number;
};
}
// 文件上传示例:上传图片
async function uploadImage(imagePath: string): Promise<string | null> {
let httpRequest = http.createHttp();
try {
// 读取文件内容
let file = fs.openSync(imagePath, fs.OpenMode.READ_ONLY);
let stat = fs.statSync(imagePath);
let buffer = new ArrayBuffer(stat.size);
fs.readSync(file.fd, buffer);
fs.closeSync(file);
// 构建multipart请求体
let formData = {
// 文件字段
'file': {
filename: imagePath.split('/').pop() || 'image.jpg',
type: 'image/jpeg',
content: buffer
},
// 其他表单字段
'userId': '12345',
'category': 'avatar'
};
let response = await httpRequest.request(
'https://api.example.com/v1/upload/image',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'multipart/form-data',
'Authorization': 'Bearer your_token'
},
extraData: formData,
connectTimeout: 60000, // 文件上传可能较慢
readTimeout: 60000
}
);
if (response.responseCode === http.ResponseCode.OK) {
let result: UploadResponse = JSON.parse(response.result as string);
if (result.code === 0) {
console.info('上传成功:', result.data.url);
return result.data.url;
}
}
return null;
} catch (error) {
let e = error as BusinessError;
console.error('上传失败:', e.message);
return null;
} finally {
httpRequest.destroy();
}
}
// 图片上传组件
@Entry
@Component
struct ImageUploadPage {
@State uploadedUrl: string = '';
@State isUploading: boolean = false;
async handleUpload() {
this.isUploading = true;
// 选择图片(需要配合Picker使用)
let imagePath = '/data/local/tmp/selected_image.jpg';
let url = await uploadImage(imagePath);
this.isUploading = false;
if (url) {
this.uploadedUrl = url;
console.info('图片已上传:', url);
}
}
build() {
Column() {
if (this.uploadedUrl) {
Image(this.uploadedUrl)
.width(200)
.height(200)
.objectFit(ImageFit.Cover)
} else {
Column() {
Text('+')
.fontSize(40)
.fontColor('#999')
Text('点击上传图片')
.fontSize(14)
.fontColor('#999')
.margin({ top: 10 })
}
.width(200)
.height(200)
.border({ width: 2, color: '#ddd', style: BorderStyle.Dashed })
.justifyContent(FlexAlign.Center)
.onClick(() => this.handleUpload())
}
if (this.isUploading) {
Text('上传中...')
.fontSize(14)
.fontColor('#4A90E2')
.margin({ top: 20 })
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
四、踩坑与注意事项
坑点一:忘记销毁请求对象
这是最常见的内存泄漏问题。每次创建http.createHttp()后,必须在请求完成后调用destroy()。
// ❌ 错误示范:忘记销毁
async function badExample() {
let httpRequest = http.createHttp();
let response = await httpRequest.request('https://api.example.com/data');
// 处理响应...
// 忘记调用 destroy()!
}
// ✅ 正确做法:使用try-finally确保销毁
async function goodExample() {
let httpRequest = http.createHttp();
try {
let response = await httpRequest.request('https://api.example.com/data');
// 处理响应...
} finally {
httpRequest.destroy(); // 无论如何都会执行
}
}
坑点二:响应体类型处理不当
response.result的类型可能是string、ArrayBuffer或Object,需要根据实际情况处理。
// 处理不同类型的响应
function handleResponse(response: http.HttpResponse) {
let result = response.result;
if (typeof result === 'string') {
// JSON字符串,需要解析
let data = JSON.parse(result);
console.info('JSON数据:', data);
} else if (result instanceof ArrayBuffer) {
// 二进制数据,如图片、文件
console.info('二进制数据,大小:', result.byteLength);
} else if (typeof result === 'object') {
// 已经是对象(部分API会自动解析)
console.info('对象数据:', result);
}
}
坑点三:超时时间设置不合理
默认超时可能不满足业务需求。网络差的场景下,短超时会导致频繁失败;大文件上传时,短超时直接报错。
// 根据场景设置合理超时
const TIMEOUT_CONFIG = {
// 普通API请求:30秒
API: { connectTimeout: 30000, readTimeout: 30000 },
// 登录认证:15秒(要求快速响应)
AUTH: { connectTimeout: 15000, readTimeout: 15000 },
// 文件上传:120秒(大文件需要更长时间)
UPLOAD: { connectTimeout: 60000, readTimeout: 120000 },
// 实时数据:10秒(要求低延迟)
REALTIME: { connectTimeout: 10000, readTimeout: 10000 }
};
坑点四:HTTP状态码判断不完整
只判断200是不够的,还要考虑201、204等其他成功状态码,以及各种错误码的处理。
// 完整的状态码处理
function handleStatusCode(response: http.HttpResponse): boolean {
const code = response.responseCode;
// 2xx 成功
if (code >= 200 && code < 300) {
return true;
}
// 根据具体状态码处理
switch (code) {
case http.ResponseCode.BAD_REQUEST:
console.error('请求参数错误');
break;
case http.ResponseCode.UNAUTHORIZED:
console.error('未授权,请先登录');
// 跳转到登录页
break;
case http.ResponseCode.FORBIDDEN:
console.error('无权限访问');
break;
case http.ResponseCode.NOT_FOUND:
console.error('资源不存在');
break;
case http.ResponseCode.INTERNAL_ERROR:
console.error('服务器内部错误');
break;
default:
console.error(`未知错误: ${code}`);
}
return false;
}
五、HarmonyOS 6适配指南
HarmonyOS 6对HTTP模块进行了若干优化和变更,需要注意以下适配点:
5.1 API变更
新增:请求优先级设置
HarmonyOS 6支持为请求设置优先级,在网络拥堵时优先处理重要请求。
// HarmonyOS 6 新增优先级配置
let httpRequest = http.createHttp();
let response = await httpRequest.request('https://api.example.com/data', {
method: http.RequestMethod.GET,
// 新增:请求优先级(HIGH/MEDIUM/LOW)
priority: http.RequestPriority.HIGH,
// 新增:是否允许蜂窝网络
usingProtocol: http.HttpProtocol.HTTP1_1
});
变更:响应头获取方式
// HarmonyOS 5及之前
let headers = response.header;
// HarmonyOS 6:响应头为Map类型,需使用get方法
let headers = response.header;
let contentType = headers.get('Content-Type');
let server = headers.get('Server');
5.2 行为变更
自动重定向处理
HarmonyOS 6默认自动处理重定向(3xx响应),无需手动处理。
// HarmonyOS 6:默认自动跟随重定向
// 如需禁用自动重定向
let response = await httpRequest.request(url, {
method: http.RequestMethod.GET,
// 禁用自动重定向
maxRedirects: 0
});
连接复用优化
系统会自动复用TCP连接,减少握手开销。无需手动配置,但要注意:
// 每个域名建议复用同一个HTTP对象池
class HttpConnectionPool {
private connections: Map<string, http.HttpRequest> = new Map();
getConnection(domain: string): http.HttpRequest {
if (!this.connections.has(domain)) {
this.connections.set(domain, http.createHttp());
}
return this.connections.get(domain)!;
}
// 应用退出时清理
destroyAll() {
this.connections.forEach((conn) => conn.destroy());
this.connections.clear();
}
}
5.3 完整适配示例
import http from '@ohos.net.http';
// HarmonyOS 6 HTTP请求封装
class HarmonyOS6HttpClient {
private request: http.HttpRequest | null = null;
// 创建请求(适配HarmonyOS 6)
async request<T>(
url: string,
options: {
method: http.RequestMethod;
data?: object | string;
headers?: Record<string, string>;
timeout?: number;
priority?: http.RequestPriority;
}
): Promise<T | null> {
this.request = http.createHttp();
try {
let requestOptions: http.HttpRequestOptions = {
method: options.method,
header: {
'Content-Type': 'application/json',
'Accept': 'application/json',
...options.headers
},
connectTimeout: options.timeout || 30000,
readTimeout: options.timeout || 30000
};
// HarmonyOS 6:设置优先级
if (options.priority) {
requestOptions.priority = options.priority;
}
// 处理请求体
if (options.data) {
if (typeof options.data === 'string') {
requestOptions.extraData = options.data;
} else {
requestOptions.extraData = JSON.stringify(options.data);
}
}
let response = await this.request.request(url, requestOptions);
// 完整状态码判断
if (response.responseCode >= 200 && response.responseCode < 300) {
if (typeof response.result === 'string') {
return JSON.parse(response.result) as T;
}
return response.result as T;
}
return null;
} catch (error) {
console.error('HTTP请求异常:', error);
return null;
} finally {
if (this.request) {
this.request.destroy();
this.request = null;
}
}
}
}
// 使用示例
let client = new HarmonyOS6HttpClient();
// 高优先级请求
let userData = await client.request<UserInfo>(
'https://api.example.com/user/profile',
{
method: http.RequestMethod.GET,
priority: http.RequestPriority.HIGH,
timeout: 15000
}
);
六、总结
HTTP请求是应用与外界通信的桥梁,掌握@ohos.net.http模块是鸿蒙开发的基本功。回顾本文要点:
核心流程:创建对象 → 配置参数 → 发送请求 → 处理响应 → 销毁对象。牢记这个五步法,就不会遗漏关键环节。
三大场景:GET请求拉数据、POST请求提数据、文件上传传资源。每种场景都有对应的配置要点和注意事项。
四个坑点:忘记销毁对象导致内存泄漏、响应体类型判断不当、超时设置不合理、状态码处理不完整。遇到问题时,先排查这四个方面。
HarmonyOS 6适配:新增请求优先级、响应头获取方式变更、自动重定向优化、连接复用机制。升级时需要相应调整代码。
网络请求看似简单,实则细节繁多。建议在实际项目中封装统一的HTTP工具类,处理通用逻辑(如token注入、错误处理、日志记录),让业务代码更专注于数据处理而非网络细节。
下一篇文章,我们将深入HTTP请求配置,探讨超时策略、重试机制、拦截器设计等进阶话题,敬请期待!
更多推荐




所有评论(0)