HarmonyOS鸿蒙Next应用中的网络请求有什么特殊处理?
HarmonyOS鸿蒙Next应用中的网络请求有什么特殊处理? 鸿蒙应用的网络请求在超时设置、错误处理、证书验证等方面需要注意,本文介绍网络请求的最佳实践和常见问题解决。
4 回复
一、基础网络请求配置
1. 创建网络请求工具(utils/request.js)
/**
* 网络请求工具
* 统一处理鸿蒙应用的网络请求
*/
// 请求配置
const REQUEST_CONFIG = {
// 基础 URL
baseURL: 'https://your-api.com/api',
// 超时时间(毫秒)
timeout: 10000,
// 请求头
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
// 是否显示加载提示
showLoading: true,
// 是否显示错误提示
showError: true,
// 重试次数
retryCount: 2,
// 重试延迟(毫秒)
retryDelay: 1000
}
// 请求队列(用于取消请求)
const requestQueue = new Map()
/**
* 统一请求方法
* @param {Object} options 请求配置
* @returns {Promise} 请求结果
*/
export function request(options = {}) {
const {
url = '',
method = 'GET',
data = {},
header = {},
timeout = REQUEST_CONFIG.timeout,
showLoading = REQUEST_CONFIG.showLoading,
showError = REQUEST_CONFIG.showError,
retry = REQUEST_CONFIG.retryCount,
cancelToken = null // 取消令牌
} = options
// 生成请求 ID
const requestId = `${Date.now()}_${Math.random()}`
// 构建完整 URL
const fullUrl = url.startsWith('http') ? url : `${REQUEST_CONFIG.baseURL}${url}`
// 合并请求头
const headers = {
...REQUEST_CONFIG.headers,
...header
}
// 显示加载提示
if (showLoading) {
uni.showLoading({
title: '加载中...',
mask: true
})
}
// 创建请求 Promise
return new Promise((resolve, reject) => {
// 检查网络状态
checkNetworkStatus().then(isConnected => {
if (!isConnected) {
if (showLoading) uni.hideLoading()
if (showError) {
uni.showToast({
title: '网络连接失败',
icon: 'none'
})
}
reject(new Error('网络连接失败'))
return
}
// 发起请求
const requestTask = uni.request({
url: fullUrl,
method: method,
data: data,
header: headers,
timeout: timeout,
success: (response) => {
// 隐藏加载提示
if (showLoading) uni.hideLoading()
// 从队列中移除
requestQueue.delete(requestId)
// 处理响应
handleResponse(response, showError)
.then(resolve)
.catch(reject)
},
fail: (error) => {
// 隐藏加载提示
if (showLoading) uni.hideLoading()
// 从队列中移除
requestQueue.delete(requestId)
// 处理错误
handleError(error, {
url: fullUrl,
method: method,
retry: retry,
showError: showError
})
.then(resolve)
.catch(reject)
}
})
// 保存到队列
requestQueue.set(requestId, requestTask)
// 设置取消令牌
if (cancelToken) {
cancelToken.promise.then(() => {
requestTask.abort()
requestQueue.delete(requestId)
reject(new Error('请求已取消'))
})
}
}).catch(error => {
if (showLoading) uni.hideLoading()
reject(error)
})
})
}
/**
* 检查网络状态
* @returns {Promise<Boolean>} 是否连接
*/
function checkNetworkStatus() {
return new Promise((resolve) => {
// #ifdef APP-HARMONY
try {
// 使用 plus API 检查网络
plus.networkinfo.getCurrentType((type) => {
// type: 0-无网络, 1-WiFi, 2-2G, 3-3G, 4-4G, 5-5G
resolve(type !== 0)
}, (error) => {
console.error('获取网络状态失败:', error)
resolve(true) // 默认认为有网络
})
} catch (error) {
console.error('检查网络状态失败:', error)
resolve(true)
}
// #endif
// #ifndef APP-HARMONY
// H5 或其他平台
uni.getNetworkType({
success: (res) => {
resolve(res.networkType !== 'none')
},
fail: () => {
resolve(true) // 默认认为有网络
}
})
// #endif
})
}
/**
* 处理响应
* @param {Object} response 响应对象
* @param {Boolean} showError 是否显示错误
* @returns {Promise} 处理结果
*/
function handleResponse(response, showError) {
return new Promise((resolve, reject) => {
const { statusCode, data } = response
// HTTP 状态码检查
if (statusCode >= 200 && statusCode < 300) {
// 业务状态码检查
if (data && typeof data === 'object') {
if (data.code === 200 || data.success) {
resolve(data.data || data)
} else {
// 业务错误
const error = new Error(data.message || data.msg || '请求失败')
error.code = data.code
error.data = data
if (showError) {
uni.showToast({
title: error.message,
icon: 'none'
})
}
reject(error)
}
} else {
resolve(data)
}
} else {
// HTTP 错误
const error = new Error(`请求失败: ${statusCode}`)
error.statusCode = statusCode
error.data = data
if (showError) {
let errorMsg = '请求失败'
if (statusCode === 404) {
errorMsg = '请求的资源不存在'
} else if (statusCode === 500) {
errorMsg = '服务器错误'
} else if (statusCode === 403) {
errorMsg = '没有权限'
} else if (statusCode === 401) {
errorMsg = '未授权,请重新登录'
// 可以触发重新登录
// handleUnauthorized()
}
uni.showToast({
title: errorMsg,
icon: 'none'
})
}
reject(error)
}
})
}
/**
* 处理错误
* @param {Object} error 错误对象
* @param {Object} options 配置选项
* @returns {Promise} 处理结果
*/
function handleError(error, options) {
return new Promise((resolve, reject) => {
const { url, method, retry, showError } = options
// 错误类型判断
let errorMessage = '网络请求失败'
if (error.errMsg) {
if (error.errMsg.includes('timeout')) {
errorMessage = '请求超时,请检查网络'
} else if (error.errMsg.includes('fail')) {
errorMessage = '网络连接失败'
} else if (error.errMsg.includes('abort')) {
errorMessage = '请求已取消'
reject(new Error(errorMessage))
return
}
}
// 重试机制
if (retry > 0) {
console.log(`请求失败,${REQUEST_CONFIG.retryDelay}ms 后重试,剩余 ${retry} 次`)
setTimeout(() => {
// 递归重试
request({
url: url,
method: method,
retry: retry - 1,
showLoading: false, // 重试时不显示加载
showError: false // 重试时不显示错误
})
.then(resolve)
.catch((retryError) => {
// 重试也失败
if (showError) {
uni.showToast({
title: errorMessage,
icon: 'none'
})
}
reject(retryError)
})
}, REQUEST_CONFIG.retryDelay)
} else {
// 不再重试
if (showError) {
uni.showToast({
title: errorMessage,
icon: 'none'
})
}
reject(new Error(errorMessage))
}
})
}
二、请求拦截和响应拦截
2. 拦截器实现
// utils/request.js 中添加拦截器
// 请求拦截器队列
const requestInterceptors = []
// 响应拦截器队列
const responseInterceptors = []
/**
* 添加请求拦截器
* @param {Function} interceptor 拦截器函数
*/
export function addRequestInterceptor(interceptor) {
requestInterceptors.push(interceptor)
}
/**
* 添加响应拦截器
* @param {Function} interceptor 拦截器函数
*/
export function addResponseInterceptor(interceptor) {
responseInterceptors.push(interceptor)
}
/**
* 执行请求拦截器
* @param {Object} config 请求配置
* @returns {Promise<Object>} 处理后的配置
*/
async function executeRequestInterceptors(config) {
let processedConfig = { ...config }
for (const interceptor of requestInterceptors) {
processedConfig = await interceptor(processedConfig) || processedConfig
}
return processedConfig
}
/**
* 执行响应拦截器
* @param {Object} response 响应对象
* @returns {Promise<Object>} 处理后的响应
*/
async function executeResponseInterceptors(response) {
let processedResponse = response
for (const interceptor of responseInterceptors) {
processedResponse = await interceptor(processedResponse) || processedResponse
}
return processedResponse
}
// 修改 request 函数,添加拦截器调用
export function request(options = {}) {
// ... 前面的代码
return new Promise(async (resolve, reject) => {
try {
// 执行请求拦截器
const processedOptions = await executeRequestInterceptors(options)
// 使用处理后的配置发起请求
const requestTask = uni.request({
// ... 使用 processedOptions
success: async (response) => {
// 执行响应拦截器
const processedResponse = await executeResponseInterceptors(response)
// 处理响应
handleResponse(processedResponse, showError)
.then(resolve)
.catch(reject)
},
// ...
})
} catch (error) {
reject(error)
}
})
}
3. 常用拦截器示例
// utils/interceptors.js
import { addRequestInterceptor, addResponseInterceptor } from '@/utils/request.js'
import { getToken, removeToken } from '@/utils/auth.js'
/**
* 添加 Token 到请求头
*/
addRequestInterceptor((config) => {
const token = getToken()
if (token) {
config.header = config.header || {}
config.header['Authorization'] = `Bearer ${token}`
}
return config
})
/**
* 处理未授权错误
*/
addResponseInterceptor((response) => {
if (response.statusCode === 401) {
// 清除 token
removeToken()
// 跳转到登录页
uni.reLaunch({
url: '/pages/login/index'
})
}
return response
})
/**
* 添加请求时间戳(防止缓存)
*/
addRequestInterceptor((config) => {
if (config.method === 'GET') {
const separator = config.url.includes('?') ? '&' : '?'
config.url = `${config.url}${separator}_t=${Date.now()}`
}
return config
})
三、请求缓存策略
4. 缓存实现
// utils/request-cache.js
// 缓存存储
const cacheStore = new Map()
// 缓存配置
const CACHE_CONFIG = {
// 默认缓存时间(毫秒)
defaultTTL: 5 * 60 * 1000, // 5分钟
// 最大缓存数量
maxSize: 100
}
/**
* 生成缓存 key
*/
function generateCacheKey(url, data) {
const dataStr = JSON.stringify(data || {})
return `${url}_${dataStr}`
}
/**
* 获取缓存
*/
export function getCache(url, data) {
const key = generateCacheKey(url, data)
const cached = cacheStore.get(key)
if (cached) {
// 检查是否过期
if (Date.now() - cached.timestamp < cached.ttl) {
return cached.data
} else {
// 过期,删除
cacheStore.delete(key)
}
}
return null
}
/**
* 设置缓存
*/
export function setCache(url, data, response, ttl = CACHE_CONFIG.defaultTTL) {
// 检查缓存数量
if (cacheStore.size >= CACHE_CONFIG.maxSize) {
// 删除最旧的缓存
const firstKey = cacheStore.keys().next().value
cacheStore.delete(firstKey)
}
const key = generateCacheKey(url, data)
cacheStore.set(key, {
data: response,
timestamp: Date.now(),
ttl: ttl
})
}
/**
* 清除缓存
*/
export function clearCache(url = null) {
if (url) {
// 清除指定 URL 的缓存
const keys = Array.from(cacheStore.keys())
keys.forEach(key => {
if (key.startsWith(url)) {
cacheStore.delete(key)
}
})
} else {
// 清除所有缓存
cacheStore.clear()
}
}
/**
* 带缓存的请求
*/
export async function cachedRequest(options = {}) {
const {
url,
data,
cache = false, // 是否使用缓存
cacheTTL = CACHE_CONFIG.defaultTTL
} = options
// 检查缓存
if (cache) {
const cachedData = getCache(url, data)
if (cachedData) {
return Promise.resolve(cachedData)
}
}
// 发起请求
const response = await request({
...options,
cache: false // 请求时不使用缓存
})
// 保存到缓存
if (cache) {
setCache(url, data, response, cacheTTL)
}
return response
}
四、网络状态监听
5. 网络状态管理
// utils/network-monitor.js
/**
* 网络状态监听
*/
class NetworkMonitor {
constructor() {
this.isConnected = true
this.networkType = 'unknown'
this.listeners = []
}
/**
* 初始化监听
*/
init() {
// #ifdef APP-HARMONY
// 监听网络状态变化
plus.networkinfo.addEventListener('change', (event) => {
const type = event.type
this.networkType = this.getNetworkTypeName(type)
this.isConnected = type !== 0
// 通知监听器
this.notifyListeners({
isConnected: this.isConnected,
networkType: this.networkType
})
})
// #endif
// #ifndef APP-HARMONY
// H5 或其他平台
uni.onNetworkStatusChange((res) => {
this.isConnected = res.isConnected
this.networkType = res.networkType
this.notifyListeners({
isConnected: this.isConnected,
networkType: this.networkType
})
})
// #endif
// 初始检查
this.checkNetworkStatus()
}
/**
* 检查网络状态
*/
checkNetworkStatus() {
// #ifdef APP-HARMONY
plus.networkinfo.getCurrentType((type) => {
this.networkType = this.getNetworkTypeName(type)
this.isConnected = type !== 0
})
// #endif
// #ifndef APP-HARMONY
uni.getNetworkType({
success: (res) => {
this.isConnected = res.networkType !== 'none'
this.networkType = res.networkType
}
})
// #endif
}
/**
* 获取网络类型名称
*/
getNetworkTypeName(type) {
const types = {
0: 'none',
1: 'wifi',
2: '2g',
3: '3g',
4: '4g',
5: '5g'
}
return types[type] || 'unknown'
}
/**
* 添加监听器
*/
addListener(listener) {
this.listeners.push(listener)
}
/**
* 移除监听器
*/
removeListener(listener) {
const index = this.listeners.indexOf(listener)
if (index > -1) {
this.listeners.splice(index, 1)
}
}
/**
* 通知监听器
*/
notifyListeners(status) {
this.listeners.forEach(listener => {
try {
listener(status)
} catch (error) {
console.error('网络状态监听器错误:', error)
}
})
}
}
// 创建单例
const networkMonitor = new NetworkMonitor()
// 在 App.vue 中初始化
export function initNetworkMonitor() {
networkMonitor.init()
}
export default networkMonitor
五、请求封装方法
6. 常用请求方法封装
// utils/request.js 中添加便捷方法
/**
* GET 请求
*/
export function get(url, data = {}, options = {}) {
return request({
url,
method: 'GET',
data,
...options
})
}
/**
* POST 请求
*/
export function post(url, data = {}, options = {}) {
return request({
url,
method: 'POST',
data,
...options
})
}
/**
* PUT 请求
*/
export function put(url, data = {}, options = {}) {
return request({
url,
method: 'PUT更多关于HarmonyOS鸿蒙Next应用中的网络请求有什么特殊处理?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
HarmonyOS Next应用网络请求基于ArkTS开发,使用@ohos.net.http模块。支持HTTP/HTTPS协议,提供异步请求能力,可通过createHttp()创建实例并调用request()发起请求。系统自动管理网络权限声明,需在module.json5中配置ohos.permission.INTERNET权限。内置网络安全机制,默认启用证书校验,支持配置自定义CA证书。
在HarmonyOS Next中,网络请求的设计更强调安全、高效和声明式开发。其核心是使用ArkTS的异步并发能力和增强的安全机制。
主要特殊处理包括:
-
超时与重试:通过
http模块的RequestOptions可灵活设置连接、读写超时。系统提供了更优雅的异步超时控制(如配合Promise.race或async/await与定时器),并建议将重试逻辑与业务解耦,置于上层拦截器或统一网络层中实现。 -
安全通信(证书验证):
- 默认强化:系统对证书验证要求更严格,开发版模拟器可能需手动处理自签名证书(如将证书放入
rawfile目录并通过securityCert模块加载)。 - SSL Pinning:支持证书锁定,可将特定证书或公钥哈希内置到应用中,防止中间人攻击。
- 仅允许加密连接:明确要求使用HTTPS,HTTP请求在默认安全配置下会受到限制。
- 默认强化:系统对证书验证要求更严格,开发版模拟器可能需手动处理自签名证书(如将证书放入
-
声明式与错误处理:
- 推荐使用
@ohos.net.http模块,其错误码(如BUSINESS_ERROR、HTTP_ERROR)定义清晰,应针对不同错误类型(网络异常、服务端错误、业务逻辑错误)进行分层捕获和处理。 - 结合ArkUI的声明式特性,网络请求状态(加载中、成功、失败)可方便地驱动UI更新,通常与
@State、@Link等装饰器配合使用。
- 推荐使用
-
性能与规范:
- 鼓励使用连接复用(默认支持)以减少开销。
- 在应用退到后台时,应及时取消未完成的网络请求(通过
http.HttpRequest的destroy方法),以节省资源。 - 网络权限需在
module.json5中明确声明(ohos.permission.INTERNET)。
最佳实践是封装统一的网络请求库,集中处理通用配置、拦截器(日志、鉴权)、错误映射和状态管理,以提升应用的可维护性和一致性。


