网络请求是现代应用的核心功能。无论是获取用户数据、上传文件还是实时通信,都需要与服务器进行网络交互。但网络请求涉及许多复杂的问题:网络延迟、错误处理、数据缓存、离线支持等。
本文将为你讲解如何在鸿蒙应用中优雅地处理网络请求和数据,包括HTTP请求、JSON解析、错误处理、数据缓存等。通过学习这些内容,你将能够构建更加健壮和高效的网络层。
HTTP请求的基础实现
鸿蒙提供了http模块来进行HTTP请求。基础的HTTP请求实现相对简单,但要做好错误处理和超时控制。
基础GET请求
typescript
import http from '@ohos.net.http'
async function fetchData(url: string): Promise<string> {
let httpRequest = http.createHttpRequest()
try {
await httpRequest.request(url, {
method: http.RequestMethod.GET,
connectTimeout: 10000,
readTimeout: 10000
})
let result = httpRequest.getResponseBody()
return result as string
} catch (error) {
console.error('Request failed:', error)
throw error
} finally {
httpRequest.destroy()
}
}
POST请求和请求头
typescript
async function postData(url: string, data: object): Promise<string> {
let httpRequest = http.createHttpRequest()
try {
await httpRequest.request(url, {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
extraData: JSON.stringify(data),
connectTimeout: 10000,
readTimeout: 10000
})
let result = httpRequest.getResponseBody()
return result as string
} catch (error) {
console.error('Request failed:', error)
throw error
} finally {
httpRequest.destroy()
}
}
JSON数据解析
从服务器获取的数据通常是JSON格式。正确的JSON解析对于数据处理至关重要。
基础JSON解析
typescript
interface User {
id: number
name: string
email: string
}
async function fetchUser(userId: number): Promise<User> {
let response = await fetchData(`https://api.example.com/users/${userId}`)
let user = JSON.parse(response) as User
return user
}
处理复杂的JSON结构
typescript
interface Post {
id: number
title: string
content: string
author: User
comments: Comment[]
}
interface Comment {
id: number
text: string
author: User
}
async function fetchPost(postId: number): Promise<Post> {
let response = await fetchData(`https://api.example.com/posts/${postId}`)
let post = JSON.parse(response) as Post
return post
}
JSON序列化
typescript
function serializeUser(user: User): string {
return JSON.stringify(user)
}
function serializeUsers(users: User[]): string {
return JSON.stringify(users, null, 2)
}
错误处理和重试机制
网络请求经常会失败。实现良好的错误处理和重试机制可以提高应用的稳定性。
基础错误处理
typescript
enum RequestError {
NETWORK_ERROR = 'NETWORK_ERROR',
TIMEOUT = 'TIMEOUT',
SERVER_ERROR = 'SERVER_ERROR',
PARSE_ERROR = 'PARSE_ERROR'
}
class RequestException extends Error {
constructor(
public code: RequestError,
message: string
) {
super(message)
}
}
async function fetchDataWithErrorHandling(url: string): Promise<string> {
try {
let httpRequest = http.createHttpRequest()
await httpRequest.request(url, {
method: http.RequestMethod.GET,
connectTimeout: 10000,
readTimeout: 10000
})
let statusCode = httpRequest.getResponseCode()
if (statusCode >= 400) {
throw new RequestException(
RequestError.SERVER_ERROR,
`Server error: ${statusCode}`
)
}
return httpRequest.getResponseBody() as string
} catch (error) {
if (error instanceof RequestException) {
throw error
}
throw new RequestException(
RequestError.NETWORK_ERROR,
'Network request failed'
)
}
}
重试机制
typescript
async function fetchDataWithRetry(
url: string,
maxRetries: number = 3
): Promise<string> {
let lastError: Error | null = null
for (let i = 0; i < maxRetries; i++) {
try {
return await fetchDataWithErrorHandling(url)
} catch (error) {
lastError = error as Error
if (i < maxRetries - 1) {
// 指数退避:等待时间随着重试次数增加而增加
let delay = Math.pow(2, i) * 1000
await new Promise(resolve => setTimeout(resolve, delay))
}
}
}
throw lastError
}
数据缓存策略
频繁的网络请求会浪费带宽和电池电量。实现合理的缓存策略可以显著提升应用性能。
内存缓存
typescript
class MemoryCache<T> {
private cache: Map<string, {data: T, timestamp: number}> = new Map()
private ttl: number // 缓存过期时间(毫秒)
constructor(ttl: number = 5 * 60 * 1000) {
this.ttl = ttl
}
set(key: string, data: T): void {
this.cache.set(key, {
data,
timestamp: Date.now()
})
}
get(key: string): T | null {
let item = this.cache.get(key)
if (!item) {
return null
}
if (Date.now() - item.timestamp > this.ttl) {
this.cache.delete(key)
return null
}
return item.data
}
clear(): void {
this.cache.clear()
}
}
let userCache = new MemoryCache<User>(10 * 60 * 1000)
async function fetchUserWithCache(userId: number): Promise<User> {
let cacheKey = `user_${userId}`
// 检查缓存
let cachedUser = userCache.get(cacheKey)
if (cachedUser) {
return cachedUser
}
// 从网络获取
let user = await fetchUser(userId)
// 存入缓存
userCache.set(cacheKey, user)
return user
}
本地存储缓存
typescript
import preferences from '@ohos.data.preferences'
class PersistentCache {
private preferences: preferences.Preferences | null = null
async init(context: any): Promise<void> {
this.preferences = await preferences.getPreferences(context, 'app_cache')
}
async set(key: string, value: string): Promise<void> {
if (!this.preferences) {
throw new Error('Cache not initialized')
}
await this.preferences.put(key, value)
await this.preferences.flush()
}
async get(key: string): Promise<string | null> {
if (!this.preferences) {
throw new Error('Cache not initialized')
}
let value = await this.preferences.get(key, null)
return value as string | null
}
async remove(key: string): Promise<void> {
if (!this.preferences) {
throw new Error('Cache not initialized')
}
await this.preferences.delete(key)
await this.preferences.flush()
}
}
let persistentCache = new PersistentCache()
async function fetchUserWithPersistentCache(userId: number): Promise<User> {
let cacheKey = `user_${userId}`
// 检查本地缓存
let cachedData = await persistentCache.get(cacheKey)
if (cachedData) {
return JSON.parse(cachedData) as User
}
// 从网络获取
let user = await fetchUser(userId)
// 存入本地缓存
await persistentCache.set(cacheKey, JSON.stringify(user))
return user
}
离线支持
应用应该能够在网络不可用时继续工作。实现离线支持需要合理的缓存策略和用户提示。
检查网络连接
typescript
import connection from '@ohos.net.connection'
async function isNetworkAvailable(): Promise<boolean> {
try {
let netHandle = await connection.getDefaultNetConnection()
let netCapabilities = await connection.getNetCapabilities(netHandle)
return netCapabilities.hasCapability(connection.NetCapsType.NET_CAPABILITY_INTERNET)
} catch (error) {
return false
}
}
async function fetchDataWithOfflineSupport(url: string): Promise<string> {
let isOnline = await isNetworkAvailable()
if (isOnline) {
try {
return await fetchDataWithRetry(url)
} catch (error) {
// 网络请求失败,尝试使用缓存
let cachedData = await persistentCache.get(url)
if (cachedData) {
console.warn('Using cached data due to network error')
return cachedData
}
throw error
}
} else {
// 离线模式,使用缓存
let cachedData = await persistentCache.get(url)
if (cachedData) {
return cachedData
}
throw new Error('No network connection and no cached data available')
}
}
最佳实践
使用拦截器统一处理请求
typescript
class HttpClient {
private requestInterceptors: Array<(config: any) => any> = []
private responseInterceptors: Array<(response: any) => any> = []
addRequestInterceptor(interceptor: (config: any) => any): void {
this.requestInterceptors.push(interceptor)
}
addResponseInterceptor(interceptor: (response: any) => any): void {
this.responseInterceptors.push(interceptor)
}
async request(url: string, config: any = {}): Promise<any> {
// 执行请求拦截器
for (let interceptor of this.requestInterceptors) {
config = interceptor(config)
}
// 执行请求
let response = await fetchDataWithRetry(url)
// 执行响应拦截器
for (let interceptor of this.responseInterceptors) {
response = interceptor(response)
}
return response
}
}
let httpClient = new HttpClient()
// 添加认证拦截器
httpClient.addRequestInterceptor((config) => {
config.header = config.header || {}
config.header['Authorization'] = 'Bearer token'
return config
})
// 添加日志拦截器
httpClient.addResponseInterceptor((response) => {
console.log('Response:', response)
return response
})
分离数据层和UI层
typescript
class UserRepository {
async getUser(userId: number): Promise<User> {
return await fetchUserWithPersistentCache(userId)
}
async getUsers(page: number, pageSize: number): Promise<User[]> {
let url = `https://api.example.com/users?page=${page}&pageSize=${pageSize}`
let response = await fetchDataWithOfflineSupport(url)
return JSON.parse(response) as User[]
}
}
@Component
struct UserList {
@State users: User[] = []
@State loading: boolean = false
@State error: string = ''
private repository = new UserRepository()
async loadUsers() {
this.loading = true
try {
this.users = await this.repository.getUsers(1, 20)
} catch (error) {
this.error = (error as Error).message
} finally {
this.loading = false
}
}
build() {
Column() {
if (this.loading) {
LoadingProgress()
} else if (this.error) {
Text(this.error)
} else {
List() {
ForEach(this.users, (user: User) => {
ListItem() {
Text(user.name)
}
})
}
}
}
}
}
总结
网络请求和数据处理是应用开发的核心。通过实现良好的错误处理、缓存策略和离线支持,你可以构建更加健壮和高效的应用。
关键要点包括:
- 使用正确的HTTP方法和请求头
- 实现完善的错误处理和重试机制
- 合理使用内存缓存和本地存储缓存
- 支持离线模式
- 分离数据层和UI层
现在就在你的项目中应用这些最佳实践吧。如果你有任何问题或想法,欢迎在评论区分享。