Harmony学习之网络请求与数据获取
Harmony学习之网络请求与数据获取
一、场景引入
小明在上一篇文章中掌握了应用生命周期管理,现在他需要从服务器获取真实的用户数据和商品信息,而不是使用模拟数据。比如用户登录后需要从服务器验证账号密码,首页需要展示从服务器获取的实时行情数据,商品详情页需要加载服务器返回的商品信息。本篇文章将系统讲解HarmonyOS的网络请求机制,帮助小明实现与后端API的数据交互。
二、网络模块导入与配置
1. 导入网络模块
在需要使用网络请求的文件中,导入http模块:
import http from '@ohos.net.http';
2. 配置网络权限
在src/main/module.json5文件中添加网络权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
三、发起HTTP请求
1. 创建请求对象
// 创建HTTP请求对象
let httpRequest = http.createHttp();
2. 发起GET请求
// 发起GET请求
httpRequest.request(
'https://api.example.com/users',
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
}
},
(err, data) => {
if (err) {
console.error('请求失败:', err);
return;
}
if (data.responseCode === 200) {
// 请求成功
const result = JSON.parse(data.result);
console.log('响应数据:', result);
} else {
console.error('请求失败,状态码:', data.responseCode);
}
}
);
3. 发起POST请求
// 发起POST请求
httpRequest.request(
'https://api.example.com/login',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json'
},
extraData: JSON.stringify({
username: 'xiaoming',
password: '123456'
})
},
(err, data) => {
if (err) {
console.error('请求失败:', err);
return;
}
if (data.responseCode === 200) {
const result = JSON.parse(data.result);
console.log('登录成功:', result);
} else {
console.error('登录失败,状态码:', data.responseCode);
}
}
);
4. 请求方法枚举
// 支持的请求方法
http.RequestMethod.GET // GET请求
http.RequestMethod.POST // POST请求
http.RequestMethod.PUT // PUT请求
http.RequestMethod.DELETE // DELETE请求
http.RequestMethod.OPTIONS // OPTIONS请求
http.RequestMethod.HEAD // HEAD请求
http.RequestMethod.TRACE // TRACE请求
http.RequestMethod.CONNECT // CONNECT请求
四、请求配置详解
1. 请求头配置
// 完整的请求头配置
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
'User-Agent': 'HarmonyOS App/1.0',
'Accept': 'application/json',
'Accept-Language': 'zh-CN',
'Cache-Control': 'no-cache'
}
}
2. 请求参数配置
// GET请求带查询参数
httpRequest.request(
'https://api.example.com/users?page=1&limit=10',
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
}
},
(err, data) => {
// 回调处理
}
);
// POST请求带请求体
httpRequest.request(
'https://api.example.com/users',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json'
},
extraData: JSON.stringify({
name: '小明',
age: 25,
email: 'xiaoming@example.com'
})
},
(err, data) => {
// 回调处理
}
);
3. 超时配置
// 设置请求超时时间(单位:毫秒)
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
},
connectTimeout: 10000, // 连接超时10秒
readTimeout: 10000 // 读取超时10秒
}
五、响应处理
1. 响应数据结构
httpRequest.request(
'https://api.example.com/users',
{
method: http.RequestMethod.GET
},
(err, data) => {
if (err) {
console.error('请求失败:', err);
return;
}
// 响应数据对象结构
console.log('响应码:', data.responseCode); // 响应状态码
console.log('响应头:', data.header); // 响应头信息
console.log('响应体:', data.result); // 响应体字符串
console.log('响应Cookies:', data.cookies); // 响应Cookies
console.log('响应长度:', data.contentLength); // 响应内容长度
console.log('响应类型:', data.responseType); // 响应类型
}
);
2. 响应状态码处理
httpRequest.request(
'https://api.example.com/users',
{
method: http.RequestMethod.GET
},
(err, data) => {
if (err) {
console.error('请求失败:', err);
return;
}
switch (data.responseCode) {
case 200:
// 请求成功
const result = JSON.parse(data.result);
this.handleSuccess(result);
break;
case 400:
console.error('请求参数错误');
break;
case 401:
console.error('未授权,请重新登录');
this.handleUnauthorized();
break;
case 403:
console.error('禁止访问');
break;
case 404:
console.error('资源不存在');
break;
case 500:
console.error('服务器内部错误');
break;
default:
console.error('未知错误,状态码:', data.responseCode);
}
}
);
3. 错误处理
httpRequest.request(
'https://api.example.com/users',
{
method: http.RequestMethod.GET
},
(err, data) => {
if (err) {
// 网络错误处理
switch (err.code) {
case http.ResponseErrorCode.NETWORK_ERROR:
console.error('网络连接失败,请检查网络设置');
break;
case http.ResponseErrorCode.TIMEOUT_ERROR:
console.error('请求超时,请稍后重试');
break;
case http.ResponseErrorCode.CANCELED_ERROR:
console.error('请求已取消');
break;
default:
console.error('网络请求失败:', err);
}
return;
}
// 业务错误处理
if (data.responseCode !== 200) {
console.error('请求失败,状态码:', data.responseCode);
return;
}
// 请求成功
const result = JSON.parse(data.result);
this.handleSuccess(result);
}
);
六、请求取消
1. 取消请求
// 发起请求
const requestTask = httpRequest.request(
'https://api.example.com/users',
{
method: http.RequestMethod.GET
},
(err, data) => {
if (err) {
if (err.code === http.ResponseErrorCode.CANCELED_ERROR) {
console.log('请求已取消');
return;
}
console.error('请求失败:', err);
return;
}
console.log('请求成功:', data.result);
}
);
// 取消请求
requestTask.abort();
2. 组件销毁时取消请求
@Component
struct UserList {
private requestTask: http.HttpTask | null = null;
aboutToAppear() {
this.loadUsers();
}
aboutToDisappear() {
// 组件销毁时取消请求
if (this.requestTask) {
this.requestTask.abort();
this.requestTask = null;
}
}
private loadUsers() {
let httpRequest = http.createHttp();
this.requestTask = httpRequest.request(
'https://api.example.com/users',
{
method: http.RequestMethod.GET
},
(err, data) => {
if (err) {
if (err.code === http.ResponseErrorCode.CANCELED_ERROR) {
console.log('请求已取消');
return;
}
console.error('请求失败:', err);
return;
}
console.log('请求成功:', data.result);
}
);
}
}
七、网络状态监听
1. 监听网络状态变化
import { network } from '@ohos.net.network';
// 监听网络状态变化
network.on('netAvailable', (data) => {
console.log('网络已连接:', data);
});
network.on('netUnavailable', () => {
console.log('网络已断开');
});
network.on('netCapabilitiesChange', (data) => {
console.log('网络能力变化:', data);
});
network.on('netConnectionPropertiesChange', (data) => {
console.log('网络连接属性变化:', data);
});
2. 获取当前网络状态
// 获取当前网络状态
network.getDefaultNet((err, data) => {
if (err) {
console.error('获取网络状态失败:', err);
return;
}
if (data) {
console.log('网络已连接,类型:', data.netCapabilities.bearerTypes);
} else {
console.log('网络未连接');
}
});
八、实战案例:封装网络请求工具类
1. 网络请求工具类
// src/main/ets/common/HttpUtil.ts
import http from '@ohos.net.http';
// 定义响应类型
interface HttpResponse {
code: number;
data: any;
message: string;
}
// 定义请求配置
interface RequestOptions {
url: string;
method: http.RequestMethod;
data?: any;
header?: Record<string, string>;
timeout?: number;
}
export class HttpUtil {
private static instance: HttpUtil;
private httpRequest: http.Http;
private constructor() {
this.httpRequest = http.createHttp();
}
public static getInstance(): HttpUtil {
if (!HttpUtil.instance) {
HttpUtil.instance = new HttpUtil();
}
return HttpUtil.instance;
}
// 发起请求
public request(options: RequestOptions): Promise<HttpResponse> {
return new Promise((resolve, reject) => {
const { url, method, data, header, timeout } = options;
const requestOptions: http.HttpRequestOptions = {
method: method,
header: {
'Content-Type': 'application/json',
...header
},
connectTimeout: timeout || 10000,
readTimeout: timeout || 10000
};
// 处理请求数据
if (data && method !== http.RequestMethod.GET) {
requestOptions.extraData = JSON.stringify(data);
}
// 处理GET请求参数
let requestUrl = url;
if (method === http.RequestMethod.GET && data) {
const params = new URLSearchParams();
Object.keys(data).forEach(key => {
params.append(key, data[key]);
});
requestUrl = `${url}?${params.toString()}`;
}
this.httpRequest.request(
requestUrl,
requestOptions,
(err, response) => {
if (err) {
reject(err);
return;
}
if (response.responseCode === 200) {
try {
const result = JSON.parse(response.result);
resolve(result);
} catch (parseError) {
reject(new Error('解析响应数据失败'));
}
} else {
reject(new Error(`请求失败,状态码: ${response.responseCode}`));
}
}
);
});
}
// GET请求
public get(url: string, params?: any, header?: Record<string, string>): Promise<HttpResponse> {
return this.request({
url: url,
method: http.RequestMethod.GET,
data: params,
header: header
});
}
// POST请求
public post(url: string, data?: any, header?: Record<string, string>): Promise<HttpResponse> {
return this.request({
url: url,
method: http.RequestMethod.POST,
data: data,
header: header
});
}
// PUT请求
public put(url: string, data?: any, header?: Record<string, string>): Promise<HttpResponse> {
return this.request({
url: url,
method: http.RequestMethod.PUT,
data: data,
header: header
});
}
// DELETE请求
public delete(url: string, data?: any, header?: Record<string, string>): Promise<HttpResponse> {
return this.request({
url: url,
method: http.RequestMethod.DELETE,
data: data,
header: header
});
}
// 设置请求拦截器
public setRequestInterceptor(interceptor: (config: RequestOptions) => RequestOptions) {
// 实际场景:实现请求拦截器
}
// 设置响应拦截器
public setResponseInterceptor(interceptor: (response: HttpResponse) => HttpResponse) {
// 实际场景:实现响应拦截器
}
}
2. 用户服务类
// src/main/ets/service/UserService.ts
import { HttpUtil } from '../common/HttpUtil';
export class UserService {
private static instance: UserService;
private httpUtil: HttpUtil;
private constructor() {
this.httpUtil = HttpUtil.getInstance();
}
public static getInstance(): UserService {
if (!UserService.instance) {
UserService.instance = new UserService();
}
return UserService.instance;
}
// 用户登录
public async login(username: string, password: string): Promise<any> {
try {
const response = await this.httpUtil.post('https://api.example.com/login', {
username: username,
password: password
});
if (response.code === 200) {
return response.data;
} else {
throw new Error(response.message || '登录失败');
}
} catch (error) {
throw error;
}
}
// 获取用户信息
public async getUserInfo(userId: string): Promise<any> {
try {
const response = await this.httpUtil.get(`https://api.example.com/users/${userId}`);
if (response.code === 200) {
return response.data;
} else {
throw new Error(response.message || '获取用户信息失败');
}
} catch (error) {
throw error;
}
}
// 更新用户信息
public async updateUserInfo(userId: string, userInfo: any): Promise<any> {
try {
const response = await this.httpUtil.put(`https://api.example.com/users/${userId}`, userInfo);
if (response.code === 200) {
return response.data;
} else {
throw new Error(response.message || '更新用户信息失败');
}
} catch (error) {
throw error;
}
}
}
3. 商品服务类
// src/main/ets/service/ProductService.ts
import { HttpUtil } from '../common/HttpUtil';
export class ProductService {
private static instance: ProductService;
private httpUtil: HttpUtil;
private constructor() {
this.httpUtil = HttpUtil.getInstance();
}
public static getInstance(): ProductService {
if (!ProductService.instance) {
ProductService.instance = new ProductService();
}
return ProductService.instance;
}
// 获取商品列表
public async getProductList(page: number = 1, limit: number = 10): Promise<any> {
try {
const response = await this.httpUtil.get('https://api.example.com/products', {
page: page,
limit: limit
});
if (response.code === 200) {
return response.data;
} else {
throw new Error(response.message || '获取商品列表失败');
}
} catch (error) {
throw error;
}
}
// 获取商品详情
public async getProductDetail(productId: string): Promise<any> {
try {
const response = await this.httpUtil.get(`https://api.example.com/products/${productId}`);
if (response.code === 200) {
return response.data;
} else {
throw new Error(response.message || '获取商品详情失败');
}
} catch (error) {
throw error;
}
}
// 搜索商品
public async searchProducts(keyword: string, page: number = 1, limit: number = 10): Promise<any> {
try {
const response = await this.httpUtil.get('https://api.example.com/products/search', {
keyword: keyword,
page: page,
limit: limit
});
if (response.code === 200) {
return response.data;
} else {
throw new Error(response.message || '搜索商品失败');
}
} catch (error) {
throw error;
}
}
}
九、实战案例:登录页面改造
1. 登录页面使用网络请求
// src/main/ets/pages/Login.ets
import router from '@ohos.router';
import { UserService } from '../service/UserService';
import { LoadingUtil } from '../common/LoadingUtil';
@Entry
@Component
struct Login {
@State username: string = '';
@State password: string = '';
@State isLoading: boolean = false;
build() {
Column({ space: 20 }) {
Text('用户登录')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 40 })
TextInput({ placeholder: '请输入用户名' })
.width('90%')
.height(50)
.backgroundColor(Color.White)
.borderRadius(8)
.borderWidth(1)
.borderColor('#E0E0E0')
.padding({ left: 15, right: 15 })
.onChange((value: string) => {
this.username = value;
})
TextInput({ placeholder: '请输入密码' })
.width('90%')
.height(50)
.backgroundColor(Color.White)
.borderRadius(8)
.borderWidth(1)
.borderColor('#E0E0E0')
.padding({ left: 15, right: 15 })
.type(InputType.Password)
.onChange((value: string) => {
this.password = value;
})
Button('登录')
.width('90%')
.height(50)
.backgroundColor('#007DFF')
.fontColor(Color.White)
.fontSize(18)
.enabled(!this.isLoading)
.onClick(() => {
this.handleLogin();
})
if (this.isLoading) {
Loading()
.color('#007DFF')
.margin({ top: 20 })
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#F5F5F5')
}
// 处理登录
private async handleLogin() {
if (!this.username || !this.password) {
promptAction.showToast({
message: '请输入用户名和密码',
duration: 2000
});
return;
}
this.isLoading = true;
try {
const userService = UserService.getInstance();
const userInfo = await userService.login(this.username, this.password);
// 登录成功,保存用户信息
await this.saveUserInfo(userInfo);
// 跳转到首页
router.replaceUrl({
url: 'pages/Home',
params: {
userId: userInfo.id,
userName: userInfo.name
}
});
promptAction.showToast({
message: '登录成功',
duration: 2000
});
} catch (error) {
console.error('登录失败:', error);
promptAction.showToast({
message: error.message || '登录失败,请稍后重试',
duration: 2000
});
} finally {
this.isLoading = false;
}
}
// 保存用户信息
private async saveUserInfo(userInfo: any) {
// 实际场景:保存用户信息到本地存储
// 这里使用模拟保存
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, 100);
});
}
}
2. 首页使用网络请求
// src/main/ets/pages/Home.ets
import router from '@ohos.router';
import { UserService } from '../service/UserService';
import { ProductService } from '../service/ProductService';
@Entry
@Component
struct Home {
@State userId: string = '';
@State userName: string = '';
@State userInfo: any = {};
@State productList: any[] = [];
@State isLoading: boolean = false;
aboutToAppear() {
const params = router.getParams();
this.userId = params?.['userId'] || '';
this.userName = params?.['userName'] || '';
this.loadUserInfo();
this.loadProductList();
}
build() {
Column({ space: 20 }) {
Text(`欢迎回来,${this.userName}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
if (this.userInfo.balance !== undefined) {
Text(`账户余额: ${this.userInfo.balance.toFixed(2)}元`)
.fontSize(18)
.margin({ bottom: 20 })
}
if (this.isLoading) {
Loading()
.color('#007DFF')
.margin({ bottom: 20 })
}
if (this.productList.length > 0) {
Text('推荐商品')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
ForEach(this.productList, (item: any) => {
Column() {
Text(item.name)
.fontSize(16)
Text(`¥${item.price.toFixed(2)}`)
.fontSize(14)
.fontColor('#FF3B30')
}
.width('100%')
.padding(10)
.backgroundColor(Color.White)
.borderRadius(8)
.margin({ bottom: 10 })
})
}
Button('刷新数据')
.width(200)
.onClick(() => {
this.refreshData();
})
Button('退出登录')
.width(200)
.backgroundColor(Color.Red)
.onClick(() => {
this.logout();
})
}
.width('100%')
.height('100%')
.padding(16)
.backgroundColor('#F5F5F5')
}
// 加载用户信息
private async loadUserInfo() {
if (!this.userId) {
return;
}
try {
const userService = UserService.getInstance();
const userInfo = await userService.getUserInfo(this.userId);
this.userInfo = userInfo;
} catch (error) {
console.error('获取用户信息失败:', error);
promptAction.showToast({
message: '获取用户信息失败,请稍后重试',
duration: 2000
});
}
}
// 加载商品列表
private async loadProductList() {
this.isLoading = true;
try {
const productService = ProductService.getInstance();
const result = await productService.getProductList(1, 10);
this.productList = result.list || [];
} catch (error) {
console.error('获取商品列表失败:', error);
promptAction.showToast({
message: '获取商品列表失败,请稍后重试',
duration: 2000
});
} finally {
this.isLoading = false;
}
}
// 刷新数据
private async refreshData() {
this.isLoading = true;
await Promise.all([
this.loadUserInfo(),
this.loadProductList()
]);
this.isLoading = false;
}
// 退出登录
private logout() {
promptAction.showDialog({
title: '确认退出登录',
message: '确定要退出登录吗?',
buttons: [
{ text: '取消', color: '#666666' },
{ text: '确定', color: '#FF3B30' }
]
}).then((result) => {
if (result.index === 1) {
// 清除登录状态
this.clearLoginStatus();
// 跳转到登录页
router.replaceUrl({
url: 'pages/Login'
});
}
});
}
// 清除登录状态
private clearLoginStatus() {
// 实际场景:清除本地存储的登录状态
}
}
十、最佳实践与注意事项
1. 网络请求最佳实践
使用Promise封装:
// ✅ 推荐:使用Promise封装网络请求
public async getData(): Promise<any> {
return new Promise((resolve, reject) => {
httpRequest.request(
'https://api.example.com/data',
{
method: http.RequestMethod.GET
},
(err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
}
);
});
}
错误处理统一:
// ✅ 推荐:统一错误处理
try {
const result = await this.getData();
this.handleSuccess(result);
} catch (error) {
this.handleError(error);
}
请求取消机制:
// ✅ 推荐:组件销毁时取消请求
aboutToDisappear() {
if (this.requestTask) {
this.requestTask.abort();
this.requestTask = null;
}
}
2. 性能优化建议
避免重复请求:
// ❌ 不推荐:每次build都发起请求
build() {
this.loadData(); // 会导致重复请求
}
// ✅ 推荐:在生命周期方法中发起请求
aboutToAppear() {
this.loadData(); // 只会在组件创建时执行一次
}
合理使用缓存:
// ✅ 推荐:使用缓存减少网络请求
private cachedData: any = null;
private async loadData() {
if (this.cachedData) {
this.data = this.cachedData;
return;
}
const result = await this.getData();
this.cachedData = result;
this.data = result;
}
分页加载:
// ✅ 推荐:分页加载数据
private async loadMore() {
if (this.isLoading || this.isEnd) {
return;
}
this.isLoading = true;
try {
const result = await this.getData(this.page + 1);
this.dataList = [...this.dataList, ...result.list];
this.page++;
this.isEnd = result.isEnd;
} catch (error) {
console.error('加载更多失败:', error);
} finally {
this.isLoading = false;
}
}
3. 安全注意事项
HTTPS加密:
// ✅ 推荐:使用HTTPS协议
const url = 'https://api.example.com/data'; // 使用HTTPS
// ❌ 不推荐:使用HTTP协议
const url = 'http://api.example.com/data'; // 不安全
敏感信息保护:
// ✅ 推荐:敏感信息不存储在代码中
const token = await this.getTokenFromStorage(); // 从安全存储获取
// ❌ 不推荐:敏感信息硬编码
const token = 'hardcoded_token'; // 不安全
输入验证:
// ✅ 推荐:验证用户输入
private validateInput(username: string, password: string): boolean {
if (!username || !password) {
return false;
}
if (username.length < 3 || username.length > 20) {
return false;
}
if (password.length < 6) {
return false;
}
return true;
}
十一、总结与行动建议
核心要点回顾
- 网络模块导入:
import http from '@ohos.net.http' - 发起请求:使用
http.createHttp().request()方法 - 请求方法:
GET、POST、PUT、DELETE等 - 请求配置:请求头、请求体、超时时间等
- 响应处理:状态码判断、错误处理、数据解析
- 请求取消:使用
abort()方法取消请求 - 网络状态监听:监听网络连接状态变化
- 最佳实践:Promise封装、错误处理、性能优化、安全防护
行动建议
- 动手实践:按照本文示例,完成登录页和首页的网络请求改造
- 封装工具类:封装自己的网络请求工具类,支持请求拦截器和响应拦截器
- 错误处理:添加完整的错误处理逻辑,包括网络错误和业务错误
- 性能优化:实现数据缓存、分页加载、请求取消等功能
- 安全防护:使用HTTPS协议、验证用户输入、保护敏感信息
- 测试验证:测试不同网络环境下的请求表现(正常网络、弱网、断网)
通过本篇文章的学习,你已经掌握了HarmonyOS网络请求与数据获取的核心能力。下一篇文章将深入讲解本地数据存储,帮助你实现数据的本地持久化存储。

浙公网安备 33010602011771号