网络请求是前端应用的重中之重,一个好的请求封装可以让开发事半功倍。本文将分享uni-app网络请求的最佳实践经验。
1. 基础请求封装
1.1 创建请求实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const request = (options = {}) => { return new Promise((resolve, reject) => { uni.request({ url: process.env.VUE_APP_BASE_API + options.url, method: options.method || 'GET', data: options.data || {}, header: options.header || {}, timeout: options.timeout || 60000, success: (res) => resolve(res.data), fail: (err) => reject(err) }) }) }
export default request
|
1.2 添加拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const httpInterceptor = { invoke(options) { if (!options.url.startsWith('http')) { options.url = baseURL + options.url } options.timeout = 10000 options.header = { ...options.header, 'source-client': 'miniapp' } const token = uni.getStorageSync('token') if (token) { options.header.Authorization = `Bearer ${token}` } return options } }
uni.addInterceptor('request', httpInterceptor) uni.addInterceptor('uploadFile', httpInterceptor)
|
2. 常见错误处理
2.1 统一错误处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| const errorHandle = (response) => { const status = response.statusCode switch (status) { case 401: uni.navigateTo({ url: '/pages/login/index' }) break case 403: uni.showToast({ title: '服务器拒绝执行', icon: 'none' }) break case 404: uni.showToast({ title: '请求的资源不存在', icon: 'none' }) break default: uni.showToast({ title: response.data.message || '请求失败', icon: 'none' }) } return Promise.reject(response) }
|
2.2 请求重试机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const retry = (fn, times = 3, delay = 1000) => { return new Promise((resolve, reject) => { function attempt() { fn().then(resolve).catch(err => { console.log(`请求失败,剩余重试次数:${times - 1}`) if (times - 1 > 0) { times-- setTimeout(attempt, delay) } else { reject(err) } }) } attempt() }) }
retry(() => request({ url: '/api/data', method: 'GET' })).then(res => { console.log('请求成功', res) }).catch(err => { console.log('重试后依然失败', err) })
|
3. Token刷新方案
3.1 无感刷新Token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| let isRefreshing = false let requests = []
const refreshToken = () => { if (isRefreshing) { return new Promise((resolve) => { requests.push((token) => { resolve(token) }) }) } isRefreshing = true return new Promise((resolve, reject) => { request({ url: '/auth/refresh', method: 'POST', data: { refresh_token: uni.getStorageSync('refresh_token') } }).then(res => { isRefreshing = false uni.setStorageSync('token', res.token) requests.forEach(cb => cb(res.token)) requests = [] resolve(res.token) }).catch(err => { isRefreshing = false uni.clearStorageSync() uni.navigateTo({ url: '/pages/login/index' }) reject(err) }) }) }
|
3.2 请求拦截中应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const httpInterceptor = { invoke(options) { if (isTokenExpired() && !options.url.includes('/auth')) { return refreshToken().then(token => { options.header.Authorization = `Bearer ${token}` return uni.request(options) }) } return options } }
|
4. 上传下载处理
4.1 文件上传封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const upload = (options = {}) => { return new Promise((resolve, reject) => { uni.uploadFile({ url: process.env.VUE_APP_BASE_API + options.url, filePath: options.filePath, name: options.name || 'file', header: { ...options.header, 'Content-Type': 'multipart/form-data' }, success: (res) => resolve(JSON.parse(res.data)), fail: (err) => reject(err) }) }) }
|
4.2 文件下载封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const download = (options = {}) => { return new Promise((resolve, reject) => { uni.downloadFile({ url: options.url, header: options.header, success: (res) => { if (res.statusCode === 200) { resolve(res.tempFilePath) } else { reject(new Error('下载失败')) } }, fail: (err) => reject(err) }) }) }
|
5. 使用示例
5.1 基础请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import request from '@/utils/request'
export const getUserInfo = () => { return request({ url: '/user/info', method: 'GET' }) }
async onLoad() { try { const userInfo = await getUserInfo() this.userInfo = userInfo } catch (err) { console.error(err) } }
|
5.2 文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const chooseAndUpload = async () => { try { const [tempFile] = await uni.chooseImage({ count: 1 }) const res = await upload({ url: '/upload', filePath: tempFile.path, name: 'file' }) console.log('上传成功', res) } catch (err) { console.error('上传失败', err) } }
|
6. 总结
- 统一封装请求方法
- 添加请求/响应拦截器
- 实现错误处理和重试机制
- 处理token刷新
- 封装上传下载方法
如果觉得文章对你有帮助,欢迎点赞、评论、分享,你的支持是我继续创作的动力!