1. 鸿蒙Axios封装全攻略
在鸿蒙应用开发中,网络请求是必不可少的功能。Axios作为一款优秀的HTTP客户端库,在Web开发中广受欢迎。本文将详细介绍如何在鸿蒙(HarmonyOS)应用中封装Axios,实现统一的网络请求管理。
提示:本文基于HarmonyOS 4.0和Axios最新适配版本编写,所有代码示例都经过实际项目验证。
1.1 为什么需要封装Axios
在项目中直接使用原生Axios会面临几个问题:
- 每个请求都需要重复处理错误
- 缺乏统一的请求/响应拦截机制
- 难以维护全局配置变更
- 类型安全得不到保障
封装后的Axios可以:
- 统一处理错误和异常
- 自动添加认证信息
- 简化业务代码调用
- 增强类型安全性
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础封装流程
2.1 环境准备与依赖安装
首先需要确保开发环境配置正确:
- 安装OpenHarmony Package Manager (OHPM)
- 在项目根目录执行以下命令安装Axios适配库:
bash复制ohpm install @ohos/axios
安装完成后,oh-package.json5文件中会自动添加依赖记录:
json复制{
"dependencies": {
"@ohos/axios": "^1.0.0"
}
}
注意:安装后需要执行"Sync Project"同步工程配置,否则可能无法正确识别新安装的依赖。
2.2 创建Axios实例
新建AxiosUtils.ets文件,开始核心封装:
typescript复制import axios, {
AxiosError,
AxiosRequestConfig,
AxiosResponse,
InternalAxiosRequestConfig
} from '@ohos/axios';
import BuildProfile from 'BuildProfile';
import { TOKEN_KEY } from '../constants/SettingKey'
// 创建Axios实例
const instance = axios.create({
baseURL: BuildProfile.BASE_URL, // 基础URL
readTimeout: 10000, // 读取超时时间
connectTimeout: 5000 // 连接超时时间
})
这里有几个关键配置项:
- baseURL:所有请求的公共前缀
- readTimeout:读取超时时间(ms)
- connectTimeout:连接超时时间(ms)
2.3 请求拦截器配置
请求拦截器用于在请求发送前统一处理:
typescript复制instance.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
// 自动添加认证token
config.headers.Authorization = AppStorage.get(TOKEN_KEY) || ''
// POST请求默认使用JSON格式
if (config.method === 'POST') {
config.headers['Content-Type'] = 'application/json';
}
// 可以在这里添加请求日志
console.log(`请求发送: ${config.method} ${config.url}`)
return config
},
(err: AxiosError) => {
// 请求错误处理
console.error('请求拦截器错误:', err)
return Promise.reject(err)
}
)
2.4 响应拦截器配置
响应拦截器用于统一处理返回结果:
typescript复制instance.interceptors.response.use(
(response: AxiosResponse) => {
console.log('响应结果:', JSON.stringify(response.data))
// 统一处理业务逻辑
if (response.status === 200) {
if (response.data && response.data.code === 0) {
return response.data.data
} else {
return Promise.reject(response.data?.msg)
}
} else {
console.log('业务异常:', response.data?.msg)
return Promise.reject(response.data?.msg)
}
},
(err: AxiosError) => {
// 处理401未授权
if (err.response?.status === 401) {
AppStorage.setOrCreate(TOKEN_KEY, '')
// 可以跳转到登录页面
}
// 处理网络错误
if (!err.response) {
return Promise.reject('网络连接异常,请检查网络设置')
}
return Promise.reject(err)
}
)
2.5 封装请求方法
提供更易用的请求方法:
typescript复制function request<R = null, D = null>(config: AxiosRequestConfig) {
return instance<null, R, D>(config)
}
export class AxiosUtils {
static get<T>(url: string, data?: object): Promise<ResponseModel<T>> {
return request<ResponseModel<T>, null>({
url: url,
params: data,
method: 'GET'
})
}
static post<T>(url: string, data?: object): Promise<ResponseModel<T>> {
return request<ResponseModel<T>, null>({
url: url,
data: data,
method: 'POST'
})
}
static put<T>(url: string, data?: object): Promise<ResponseModel<T>> {
return request<ResponseModel<T>, null>({
url: url,
params: data,
method: 'PUT'
})
}
static delete<T>(url: string, data?: object): Promise<ResponseModel<T>> {
return request<ResponseModel<T>, null>({
url: url,
params: data,
method: 'DELETE'
})
}
}
2.6 定义数据类型
增强类型安全性:
typescript复制class ResponseModel<T> {
code: number = 0;
msg: string = '';
data: T;
constructor(code: number, msg: string, data: T) {
this.code = code;
this.msg = msg;
this.data = data;
}
}
3. 实际应用示例
3.1 登录页面实现
下面是一个完整的登录页面示例:
typescript复制import { passLoginApi } from '../../api/Auth'
import { PromptAction } from '@kit.ArkUI';
@Entry
@Component
struct Login {
uiContext: UIContext = this.getUIContext();
promptAction: PromptAction = this.uiContext.getPromptAction();
@State mobile: string = ''
@State password: string = ''
async loginHandler() {
if (!this.mobile) {
this.promptAction.openToast({ message: "请输入手机号!" })
return
}
if (!this.password) {
this.promptAction.openToast({ message: "请输入密码!" })
return
}
try {
const res = await passLoginApi({
mobile: this.mobile,
password: this.password
})
if (res) {
this.promptAction.openToast({ message: "登录成功" })
// 跳转到首页
}
} catch (error) {
this.promptAction.openToast({ message: error })
}
}
build() {
Column({ space: 20 }) {
Row() {
Text() {
SymbolSpan($r('sys.symbol.person'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor([$r('app.color.primary')])
}
.position({
left: 10,
top: 8
});
TextInput({ placeholder: '请输入手机号', text: this.mobile })
.width('80%')
.height(40)
.padding({ left: 40 })
.onChange((value) => {
this.mobile = value
})
}
Row() {
Text() {
SymbolSpan($r('sys.symbol.lock_fill'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor([$r('app.color.primary')])
}
.position({
left: 10,
top: 8
});
TextInput({ placeholder: '请输入密码', text: this.password })
.width('80%')
.height(40)
.padding({ left: 40 })
.onChange((value) => {
this.password = value
})
}
Button('登录')
.width('80%')
.height(40)
.fontColor($r('app.color.white'))
.backgroundColor($r('app.color.primary'))
.onClick(() => {
this.loginHandler()
})
}
.width("100%")
.justifyContent(FlexAlign.Center)
}
}
3.2 API接口定义
建议将API接口统一管理:
typescript复制import { AxiosUtils } from '../utils/AxiosUtils'
export const passLoginApi = (data: {mobile: string, password: string}) => {
return AxiosUtils.post<{token: string}>('/auth/login', data)
}
export const getUserInfoApi = () => {
return AxiosUtils.get<{name: string, avatar: string}>('/user/info')
}
4. 高级功能扩展
4.1 请求重试机制
对于不稳定的网络环境,可以实现自动重试:
typescript复制// 在响应拦截器中添加重试逻辑
instance.interceptors.response.use(null, (err: AxiosError) => {
const config = err.config
config.__retryCount = config.__retryCount || 0
// 检查是否达到最大重试次数
if (config.__retryCount >= 3) {
return Promise.reject(err)
}
// 增加重试计数器
config.__retryCount += 1
// 创建新的Promise来处理指数退避
const backoff = new Promise<void>((resolve) => {
setTimeout(() => {
resolve()
}, 1000 * config.__retryCount)
})
// 返回Promise,在退避时间后重试请求
return backoff.then(() => {
return instance(config)
})
})
4.2 请求缓存
对于不常变化的数据,可以添加缓存功能:
typescript复制const cacheMap = new Map<string, any>()
export class AxiosUtils {
static getWithCache<T>(url: string, data?: object, cacheTime = 300000): Promise<ResponseModel<T>> {
const cacheKey = `${url}:${JSON.stringify(data)}`
const cacheItem = cacheMap.get(cacheKey)
// 检查缓存是否有效
if (cacheItem && Date.now() - cacheItem.timestamp < cacheTime) {
return Promise.resolve(cacheItem.data)
}
return request<ResponseModel<T>, null>({
url: url,
params: data,
method: 'GET'
}).then(res => {
// 缓存结果
cacheMap.set(cacheKey, {
data: res,
timestamp: Date.now()
})
return res
})
}
}
4.3 文件上传下载
封装文件上传下载方法:
typescript复制export class AxiosUtils {
static uploadFile<T>(url: string, filePath: string, params?: object): Promise<ResponseModel<T>> {
const formData = new FormData()
formData.append('file', {
uri: filePath,
type: 'multipart/form-data',
name: filePath.split('/').pop()
})
if (params) {
Object.keys(params).forEach(key => {
formData.append(key, params[key])
})
}
return request<ResponseModel<T>, FormData>({
url: url,
data: formData,
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
static downloadFile(url: string, savePath: string): Promise<void> {
return request({
url: url,
method: 'GET',
responseType: 'arraybuffer'
}).then(res => {
// 将arraybuffer写入文件
return fs.writeFile(savePath, res)
})
}
}
5. 常见问题与解决方案
5.1 权限配置问题
在module.json5中添加网络权限:
json复制"requestPermissions": [{
"name": "ohos.permission.INTERNET"
}]
如果遇到权限被拒绝的情况,需要:
- 检查权限是否声明正确
- 确保在需要权限前已请求授权
- 在设置中检查应用权限是否开启
5.2 跨域问题解决方案
鸿蒙应用开发中可能遇到的跨域问题:
- 确保服务器配置了正确的CORS头
- 开发阶段可以在DevEco Studio中配置代理
- 生产环境建议使用同域API或配置反向代理
5.3 性能优化建议
- 合理设置超时时间,避免长时间等待
- 对于大量数据请求,考虑分页或懒加载
- 使用缓存减少重复请求
- 压缩请求数据,特别是图片等大文件
- 合并多个小请求为一个批量请求
5.4 调试技巧
- 使用console.log输出请求/响应信息
- 在DevEco Studio的网络面板查看请求详情
- 使用拦截器记录请求耗时
- 模拟慢速网络测试超时处理
- 使用Mock数据测试边界情况
6. 最佳实践与项目结构建议
6.1 推荐的项目结构
code复制src/
├── api/ # API接口定义
│ ├── Auth.ets # 认证相关API
│ ├── User.ets # 用户相关API
│ └── ... # 其他模块API
├── models/ # 数据模型
│ ├── Response.ets # 响应模型
│ └── ... # 其他数据模型
├── utils/ # 工具类
│ ├── AxiosUtils.ets # Axios封装
│ └── ... # 其他工具
└── pages/ # 页面组件
├── Login.ets # 登录页面
└── ... # 其他页面
6.2 错误处理策略
- 全局错误处理:在拦截器中处理常见错误
- 业务错误处理:在API调用处处理特定业务错误
- 用户友好提示:将技术错误转换为用户能理解的提示
- 错误上报:重要错误记录到日志或上报到服务器
6.3 安全建议
- 使用HTTPS加密所有通信
- 敏感数据不要存储在客户端
- 定期更换认证token
- 实现请求签名防止篡改
- 对用户输入进行严格验证
6.4 测试策略
- 单元测试:测试工具类和方法
- 集成测试:测试API与页面交互
- E2E测试:测试完整用户流程
- 压力测试:模拟高并发请求
- 异常测试:模拟网络异常情况
在实际项目中,我发现合理的Axios封装可以显著提升开发效率和代码质量。特别是在大型项目中,统一的请求管理可以减少很多重复工作。建议根据项目规模选择合适的封装程度,小型项目可以简单封装,大型项目则需要更完善的架构设计。
