HarmonyOS 5开发从入门到精通(七):网络请求与数据获取
HarmonyOS 5开发从入门到精通(七):网络请求与数据获取
网络请求是现代移动应用与外界交互的核心能力,无论是获取数据、提交表单还是实时通信,都离不开网络请求的支持。HarmonyOS 5提供了强大的网络请求能力,支持HTTP/HTTPS、WebSocket等多种协议。本篇将深入讲解网络请求的实现、优化和实战应用。
一、网络请求基础配置
1.1 权限配置
在发起网络请求前,需要在配置文件中声明网络权限:
// entry/src/main/module.json5
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:internet_permission_reason"
},
{
"name": "ohos.permission.GET_NETWORK_INFO",
"reason": "$string:network_info_permission_reason"
}
]
}
}
1.2 网络状态检测
在发起请求前,建议先检测网络状态:
import netConnection from '@ohos.net.connection';
import { BusinessError } from '@ohos.base';
class NetworkUtils {
// 检测网络是否可用
static async isNetworkAvailable(): Promise<boolean> {
try {
const netCap = await netConnection.getDefaultNet();
if (!netCap) {
return false;
}
const netInfo = await netConnection.getConnectionProperties(netCap);
return netInfo.isAvailable;
} catch (error) {
console.error(`检测网络失败: ${(error as BusinessError).message}`);
return false;
}
}
// 获取网络类型
static getNetworkType(): string {
const netCap = netConnection.getDefaultNetSync();
return netCap ? netCap.type : 'unknown';
}
}
二、HTTP请求基础
2.1 导入HTTP模块
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
2.2 GET请求
// 创建HTTP请求实例
let httpRequest = http.createHttp();
// 发起GET请求
httpRequest.request(
'https://jsonplaceholder.typicode.com/posts',
{
method: http.RequestMethod.GET,
connectTimeout: 10000, // 连接超时10秒
readTimeout: 15000, // 读取超时15秒
header: {
'Content-Type': 'application/json'
}
},
(err: BusinessError, data: http.HttpResponse) => {
if (err) {
console.error(`请求失败: ${err.message}`);
return;
}
if (data.responseCode === 200) {
const result = JSON.parse(data.result as string);
console.info('请求成功:', result);
} else {
console.error(`请求失败,状态码: ${data.responseCode}`);
}
}
);
2.3 POST请求
// 发起POST请求
httpRequest.request(
'https://jsonplaceholder.typicode.com/posts',
{
method: http.RequestMethod.POST,
connectTimeout: 10000,
readTimeout: 15000,
header: {
'Content-Type': 'application/json'
},
extraData: JSON.stringify({
title: '测试标题',
body: '测试内容',
userId: 1
})
},
(err: BusinessError, data: http.HttpResponse) => {
if (err) {
console.error(`请求失败: ${err.message}`);
return;
}
if (data.responseCode === 201) {
const result = JSON.parse(data.result as string);
console.info('创建成功:', result);
} else {
console.error(`创建失败,状态码: ${data.responseCode}`);
}
}
);
2.4 响应头事件监听
// 订阅响应头事件
httpRequest.on('headersReceive', (header: Object) => {
console.info('响应头:', header);
});
// 取消订阅
httpRequest.off('headersReceive');
2.5 请求销毁
// 销毁请求实例
httpRequest.destroy();
三、网络请求封装优化
3.1 统一请求封装
// utils/request.ts
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
interface RequestOptions {
url: string;
method: http.RequestMethod;
data?: any;
headers?: Record<string, string>;
connectTimeout?: number;
readTimeout?: number;
}
interface Response<T = any> {
code: number;
data: T;
message: string;
}
class Request {
private static instance: Request;
private httpRequest: http.HttpRequest;
private constructor() {
this.httpRequest = http.createHttp();
}
static getInstance(): Request {
if (!Request.instance) {
Request.instance = new Request();
}
return Request.instance;
}
async request<T>(options: RequestOptions): Promise<Response<T>> {
return new Promise((resolve, reject) => {
this.httpRequest.request(
options.url,
{
method: options.method,
connectTimeout: options.connectTimeout || 10000,
readTimeout: options.readTimeout || 15000,
header: {
'Content-Type': 'application/json',
...options.headers
},
extraData: options.data ? JSON.stringify(options.data) : undefined
},
(err: BusinessError, data: http.HttpResponse) => {
if (err) {
reject(new Error(`网络请求失败: ${err.message}`));
return;
}
if (data.responseCode >= 200 && data.responseCode < 300) {
try {
const result = data.result ? JSON.parse(data.result as string) : {};
resolve({
code: data.responseCode,
data: result,
message: '请求成功'
});
} catch (parseError) {
reject(new Error('数据解析失败'));
}
} else {
reject(new Error(`请求失败,状态码: ${data.responseCode}`));
}
}
);
});
}
// GET请求
async get<T>(url: string, params?: Record<string, any>, headers?: Record<string, string>): Promise<Response<T>> {
let requestUrl = url;
if (params) {
const queryParams = new URLSearchParams(params).toString();
requestUrl = `${url}?${queryParams}`;
}
return this.request<T>({
url: requestUrl,
method: http.RequestMethod.GET,
headers
});
}
// POST请求
async post<T>(url: string, data?: any, headers?: Record<string, string>): Promise<Response<T>> {
return this.request<T>({
url,
method: http.RequestMethod.POST,
data,
headers
});
}
// PUT请求
async put<T>(url: string, data?: any, headers?: Record<string, string>): Promise<Response<T>> {
return this.request<T>({
url,
method: http.RequestMethod.PUT,
data,
headers
});
}
// DELETE请求
async delete<T>(url: string, headers?: Record<string, string>): Promise<Response<T>> {
return this.request<T>({
url,
method: http.RequestMethod.DELETE,
headers
});
}
destroy() {
this.httpRequest.destroy();
}
}
export default Request.getInstance();
3.2 拦截器实现
// utils/interceptor.ts
import { BusinessError } from '@ohos.base';
interface Interceptor {
onRequest?: (config: any) => Promise<any> | any;
onResponse?: (response: any) => Promise<any> | any;
onError?: (error: BusinessError) => Promise<any> | any;
}
class InterceptorManager {
private interceptors: Interceptor[] = [];
use(interceptor: Interceptor): number {
this.interceptors.push(interceptor);
return this.interceptors.length - 1;
}
eject(id: number): void {
this.interceptors.splice(id, 1);
}
async runRequestInterceptors(config: any): Promise<any> {
let currentConfig = config;
for (const interceptor of this.interceptors) {
if (interceptor.onRequest) {
currentConfig = await interceptor.onRequest(currentConfig);
}
}
return currentConfig;
}
async runResponseInterceptors(response: any): Promise<any> {
let currentResponse = response;
for (const interceptor of this.interceptors) {
if (interceptor.onResponse) {
currentResponse = await interceptor.onResponse(currentResponse);
}
}
return currentResponse;
}
async runErrorInterceptors(error: BusinessError): Promise<any> {
let currentError = error;
for (const interceptor of this.interceptors) {
if (interceptor.onError) {
currentError = await interceptor.onError(currentError);
}
}
return currentError;
}
}
export default new InterceptorManager();
3.3 带拦截器的请求封装
// utils/requestWithInterceptor.ts
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
import interceptorManager from './interceptor';
class RequestWithInterceptor {
private httpRequest: http.HttpRequest;
constructor() {
this.httpRequest = http.createHttp();
}
async request<T>(options: any): Promise<T> {
try {
// 执行请求拦截器
const config = await interceptorManager.runRequestInterceptors(options);
return new Promise((resolve, reject) => {
this.httpRequest.request(
config.url,
{
method: config.method,
connectTimeout: config.connectTimeout || 10000,
readTimeout: config.readTimeout || 15000,
header: config.headers,
extraData: config.data ? JSON.stringify(config.data) : undefined
},
async (err: BusinessError, data: http.HttpResponse) => {
if (err) {
const error = await interceptorManager.runErrorInterceptors(err);
reject(error);
return;
}
try {
let responseData = data.result ? JSON.parse(data.result as string) : {};
if (data.responseCode >= 200 && data.responseCode < 300) {
// 执行响应拦截器
responseData = await interceptorManager.runResponseInterceptors({
code: data.responseCode,
data: responseData,
message: '请求成功'
});
resolve(responseData);
} else {
const error = new Error(`请求失败,状态码: ${data.responseCode}`);
const processedError = await interceptorManager.runErrorInterceptors(error as BusinessError);
reject(processedError);
}
} catch (parseError) {
const error = new Error('数据解析失败');
const processedError = await interceptorManager.runErrorInterceptors(error as BusinessError);
reject(processedError);
}
}
);
});
} catch (error) {
const processedError = await interceptorManager.runErrorInterceptors(error as BusinessError);
throw processedError;
}
}
destroy() {
this.httpRequest.destroy();
}
}
export default RequestWithInterceptor;
四、网络请求优化策略
4.1 多级缓存架构
// utils/cache.ts
interface CacheItem {
data: any;
timestamp: number;
ttl: number; // 缓存有效时间(毫秒)
}
class CacheManager {
private memoryCache: Map<string, CacheItem> = new Map();
private static instance: CacheManager;
static getInstance(): CacheManager {
if (!CacheManager.instance) {
CacheManager.instance = new CacheManager();
}
return CacheManager.instance;
}
set(key: string, data: any, ttl: number = 5 * 60 * 1000): void {
this.memoryCache.set(key, {
data,
timestamp: Date.now(),
ttl
});
}
get(key: string): any | null {
const item = this.memoryCache.get(key);
if (!item) {
return null;
}
// 检查缓存是否过期
if (Date.now() - item.timestamp > item.ttl) {
this.memoryCache.delete(key);
return null;
}
return item.data;
}
delete(key: string): void {
this.memoryCache.delete(key);
}
clear(): void {
this.memoryCache.clear();
}
}
export default CacheManager.getInstance();
4.2 请求合并与去重
// utils/requestManager.ts
import { BusinessError } from '@ohos.base';
class RequestManager {
private pendingRequests: Map<string, Promise<any>> = new Map();
private static instance: RequestManager;
static getInstance(): RequestManager {
if (!RequestManager.instance) {
RequestManager.instance = new RequestManager();
}
return RequestManager.instance;
}
async execute<T>(key: string, request: () => Promise<T>): Promise<T> {
// 如果已有相同请求正在进行,直接返回该请求的Promise
if (this.pendingRequests.has(key)) {
return this.pendingRequests.get(key) as Promise<T>;
}
const promise = request()
.then(response => {
this.pendingRequests.delete(key);
return response;
})
.catch(error => {
this.pendingRequests.delete(key);
throw error;
});
this.pendingRequests.set(key, promise);
return promise;
}
cancel(key: string): void {
this.pendingRequests.delete(key);
}
clear(): void {
this.pendingRequests.clear();
}
}
export default RequestManager.getInstance();
4.3 智能重试机制
// utils/retry.ts
import { BusinessError } from '@ohos.base';
interface RetryOptions {
maxRetries?: number;
retryDelay?: number;
shouldRetry?: (error: BusinessError) => boolean;
}
class RetryManager {
static async withRetry<T>(
operation: () => Promise<T>,
options: RetryOptions = {}
): Promise<T> {
const {
maxRetries = 3,
retryDelay = 1000,
shouldRetry = (error: BusinessError) => true
} = options;
let lastError: BusinessError;
let attempt = 0;
while (attempt <= maxRetries) {
try {
return await operation();
} catch (error) {
lastError = error as BusinessError;
attempt++;
if (attempt > maxRetries || !shouldRetry(lastError)) {
break;
}
// 指数退避策略
const delay = retryDelay * Math.pow(2, attempt - 1);
console.info(`请求失败,第${attempt}次重试,等待${delay}ms`);
await this.delay(delay);
}
}
throw lastError;
}
private static delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
export default RetryManager;
五、网络请求实战案例
5.1 用户登录案例
// services/authService.ts
import request from '../utils/request';
interface LoginParams {
username: string;
password: string;
}
interface LoginResponse {
token: string;
userInfo: {
id: number;
username: string;
avatar: string;
};
}
class AuthService {
async login(params: LoginParams): Promise<LoginResponse> {
const response = await request.post<LoginResponse>('/api/auth/login', params);
return response.data;
}
async logout(): Promise<void> {
await request.post('/api/auth/logout');
}
async getProfile(): Promise<any> {
const response = await request.get('/api/auth/profile');
return response.data;
}
}
export default new AuthService();
5.2 商品列表案例
// services/productService.ts
import request from '../utils/request';
import CacheManager from '../utils/cache';
interface Product {
id: number;
name: string;
price: number;
image: string;
description: string;
}
interface ProductListParams {
page: number;
pageSize: number;
category?: string;
keyword?: string;
}
class ProductService {
private cacheKey = 'product_list';
async getProducts(params: ProductListParams): Promise<Product[]> {
const cacheKey = `${this.cacheKey}_${JSON.stringify(params)}`;
// 检查缓存
const cachedData = CacheManager.get(cacheKey);
if (cachedData) {
return cachedData;
}
const response = await request.get<Product[]>('/api/products', params);
// 缓存数据,有效期5分钟
CacheManager.set(cacheKey, response.data, 5 * 60 * 1000);
return response.data;
}
async getProductDetail(id: number): Promise<Product> {
const cacheKey = `product_detail_${id}`;
const cachedData = CacheManager.get(cacheKey);
if (cachedData) {
return cachedData;
}
const response = await request.get<Product>(`/api/products/${id}`);
// 缓存详情,有效期10分钟
CacheManager.set(cacheKey, response.data, 10 * 60 * 1000);
return response.data;
}
async searchProducts(keyword: string): Promise<Product[]> {
const response = await request.get<Product[]>('/api/products/search', { keyword });
return response.data;
}
}
export default new ProductService();
5.3 文件上传案例
// services/uploadService.ts
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
class UploadService {
async uploadFile(fileUri: string, onProgress?: (progress: number) => void): Promise<string> {
return new Promise((resolve, reject) => {
const httpRequest = http.createHttp();
httpRequest.request(
'/api/upload',
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'multipart/form-data'
},
extraData: {
file: {
filename: 'image.jpg',
uri: fileUri,
type: 'image/jpeg'
}
}
},
(err: BusinessError, data: http.HttpResponse) => {
if (err) {
reject(new Error(`上传失败: ${err.message}`));
return;
}
if (data.responseCode === 200) {
const result = JSON.parse(data.result as string);
resolve(result.url);
} else {
reject(new Error(`上传失败,状态码: ${data.responseCode}`));
}
}
);
// 监听上传进度
httpRequest.on('dataProgress', (progress: number) => {
if (onProgress) {
onProgress(progress);
}
});
});
}
}
export default new UploadService();
六、网络请求状态管理
6.1 加载状态管理
// types/loadState.ts
export class LoadState<T> {
static readonly INITIAL = new LoadState<any>('initial', undefined, undefined);
static readonly LOADING = new LoadState<any>('loading', undefined, undefined);
static readonly SUCCESS = <T>(data: T) => new LoadState<T>('success', data, undefined);
static readonly ERROR = (error: Error) => new LoadState<any>('error', undefined, error);
private constructor(
private readonly status: 'initial' | 'loading' | 'success' | 'error',
private readonly data?: T,
private readonly error?: Error
) {}
isInitial(): boolean {
return this.status === 'initial';
}
isLoading(): boolean {
return this.status === 'loading';
}
isSuccess(): boolean {
return this.status === 'success';
}
isError(): boolean {
return this.status === 'error';
}
getData(): T | undefined {
return this.data;
}
getError(): Error | undefined {
return this.error;
}
map<U>(mapper: (data: T) => U): LoadState<U> {
if (this.isSuccess()) {
return LoadState.SUCCESS(mapper(this.data!));
}
return new LoadState<U>(this.status, undefined, this.error);
}
}
6.2 状态管理ViewModel
// viewmodels/productViewModel.ts
import { ViewModel } from '@ohos.arkui';
import productService from '../services/productService';
import { LoadState } from '../types/loadState';
interface Product {
id: number;
name: string;
price: number;
image: string;
description: string;
}
export class ProductViewModel extends ViewModel {
@State products: LoadState<Product[]> = LoadState.INITIAL;
@State currentPage: number = 1;
@State hasMore: boolean = true;
@State isLoadingMore: boolean = false;
async loadProducts(page: number = 1) {
try {
this.products = LoadState.LOADING;
const data = await productService.getProducts({
page,
pageSize: 20
});
this.products = LoadState.SUCCESS(data);
this.currentPage = page;
this.hasMore = data.length === 20;
} catch (error) {
this.products = LoadState.ERROR(error as Error);
}
}
async loadMore() {
if (this.isLoadingMore || !this.hasMore) {
return;
}
try {
this.isLoadingMore = true;
const nextPage = this.currentPage + 1;
const data = await productService.getProducts({
page: nextPage,
pageSize: 20
});
if (this.products.isSuccess()) {
const currentData = this.products.getData() || [];
this.products = LoadState.SUCCESS([...currentData, ...data]);
} else {
this.products = LoadState.SUCCESS(data);
}
this.currentPage = nextPage;
this.hasMore = data.length === 20;
} catch (error) {
console.error('加载更多失败:', error);
} finally {
this.isLoadingMore = false;
}
}
async refresh() {
await this.loadProducts(1);
}
}
七、网络请求最佳实践
7.1 错误处理策略
// utils/errorHandler.ts
import { BusinessError } from '@ohos.base';
import prompt from '@ohos.prompt';
class ErrorHandler {
static handleNetworkError(error: BusinessError): void {
const errorCode = error.code;
switch (errorCode) {
case 401:
// 未授权,跳转到登录页
this.showToast('登录已过期,请重新登录');
break;
case 403:
this.showToast('权限不足');
break;
case 404:
this.showToast('请求的资源不存在');
break;
case 500:
this.showToast('服务器内部错误');
break;
case 503:
this.showToast('服务不可用');
break;
default:
this.showToast('网络请求失败,请稍后重试');
}
}
static handleBusinessError(error: any): void {
if (error.message) {
this.showToast(error.message);
} else {
this.showToast('操作失败,请稍后重试');
}
}
private static showToast(message: string): void {
prompt.showToast({
message,
duration: 2000
});
}
}
export default ErrorHandler;
7.2 弱网环境适配
// utils/networkAdapter.ts
import netConnection from '@ohos.net.connection';
import { BusinessError } from '@ohos.base';
class NetworkAdapter {
private static instance: NetworkAdapter;
private currentNetworkType: string = 'unknown';
static getInstance(): NetworkAdapter {
if (!NetworkAdapter.instance) {
NetworkAdapter.instance = new NetworkAdapter();
}
return NetworkAdapter.instance;
}
constructor() {
this.initNetworkListener();
}
private initNetworkListener(): void {
netConnection.on('netAvailable', (netHandle: netConnection.NetHandle) => {
this.updateNetworkType();
});
netConnection.on('netCapabilitiesChange', () => {
this.updateNetworkType();
});
}
private async updateNetworkType(): Promise<void> {
try {
const netCap = await netConnection.getDefaultNet();
if (netCap) {
this.currentNetworkType = netCap.type;
}
} catch (error) {
console.error('获取网络类型失败:', error);
}
}
isWeakNetwork(): boolean {
return this.currentNetworkType === 'cellular' || this.currentNetworkType === 'unknown';
}
getNetworkType(): string {
return this.currentNetworkType;
}
shouldUseLowQualityImage(): boolean {
return this.isWeakNetwork();
}
shouldReduceRequest(): boolean {
return this.isWeakNetwork();
}
}
export default NetworkAdapter.getInstance();
八、总结
通过本篇学习,您已经掌握了:
✅ 网络请求基础:GET、POST请求的实现,权限配置和网络状态检测
✅ 请求封装优化:统一请求封装、拦截器机制、多级缓存架构
✅ 性能优化策略:请求合并与去重、智能重试机制、弱网环境适配
✅ 状态管理:加载状态管理、ViewModel模式的应用
✅ 实战案例:用户登录、商品列表、文件上传等完整案例
✅ 最佳实践:错误处理策略、网络适配、性能监控
关键知识点回顾:
- 使用
@ohos.net.http模块发起HTTP请求 - 合理设置超时时间和请求头
- 通过拦截器统一处理Token、错误等逻辑
- 使用多级缓存减少网络请求次数
- 通过状态管理实现优雅的加载和错误处理
- 弱网环境下自动降级策略
下一篇我们将学习本地数据存储,掌握如何在设备本地持久化存储应用数据,包括Preferences、关系型数据库等存储方案。
浙公网安备 33010602011771号