uni.request 二次封装
2026年1月30日 10:30
基于uni.request api进行二次封装
核心功能
- 响应内容格式和请求参数格式类型定义
- 请求拦截器与响应拦截器配置
- 设置请求头和params参数处理
- 加载提示与自定义提示文本
- 错误统一处理
- 接口缓存
- 取消请求功能
- 失败自动重试机制
- 并发请求控制
核心代码
// 响应内容格式
export interface InResult<T> {
code: number | string
message: string
success: boolean
# data: T
}
// 请求参数格式
interface RequestOptions {
baseURL?: string
url: string
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
data?: Record<string, any>
params?: Record<string, any>
header?: Record<string, string>
loading?: boolean // 是否显示加载中提示
loadingText?: string // 加载中提示文本
removeToken?: boolean // 是否移除token
cache?: boolean // 是否缓存响应结果
returnResponse?: boolean // 直接返回响应
}
// 设置请求头
function setRequestHeaders(url: string) {
const LOGIN_URL = '/auth/oauth2/token'
if (url.includes(LOGIN_URL)) {
return { Authorization: `Basic Z2VveHNwYWNlOmdlb3hzcGFjZQ==` }
}
const token = uni.getStorageSync('token')
if (token) {
return { Authorization: `Bearer ${token}` }
}
return {} as Record<string, string>
}
// 请求和响应拦截器
function requestAndResponseInterceptor() {
uni.addInterceptor('request', {
// 调用前置拦截器
invoke(options: RequestOptions) {
if (options.loading) {
uni.showLoading({
title: options.loadingText || '加载中...',
mask: true,
})
}
options.header = {
...options.header,
...setRequestHeaders(options.url),
}
// 移除token
if (options.removeToken) {
delete options.header.Authorization
}
// 处理params 参数
if (options.params) {
const urlPrams: string[] = []
Object.keys(options.params).forEach((key) => {
urlPrams.push(`${key}=${options.params![key]}`)
})
if (options.url.includes('?')) {
options.url += urlPrams.join('&')
}
else {
options.url += `?${urlPrams.join('&')}`
}
}
return options
},
// 调用后置拦截器
success(res) {
return res
},
fail(err) {
uni.showToast({
title: '网络请求失败',
icon: 'none',
})
return Promise.reject(err)
},
complete(option) {
console.log('option.errMsg', option.errMsg)
setTimeout(() => {
uni.hideLoading()
}, 15000)
},
})
}
// 调用请求拦截器和响应拦截器
requestAndResponseInterceptor()
const cacheMap = new Map<string, any>()
// 封装网络请求
export async function request(options: RequestOptions): Promise<any> {
const { baseURL, url, header = {}, cache, returnResponse } = options
// 合并配置
const config = {
...options,
url: url.startsWith('http') ? options.url : baseURL + options.url,
header: {
'Content-Type': 'application/json',
...header, // 允许自定义header
},
timeout: 10000, // 超时时间(ms)
}
if (cache) {
if (cacheMap.has(url)) {
return cacheMap.get(url)
}
}
try {
const response = await uni.request(config)
if (options.loading) {
uni.hideLoading()
}
// 响应拦截器
if (response.statusCode === 200) {
const data = returnResponse ? response : response.data
if (cache) {
cacheMap.set(url, data)
}
// @ts-expect-error 判断异常
if (response.data.code !== '20000') {
// @ts-expect-error 判断异常
toast(response.data.msg, {
icon: 'fail',
})
}
return data
}
if (response.statusCode === 401) {
toast('登录已过期,请重新登录!', {}, () => {
uni.redirectTo({
url: '/user/login/index',
})
})
}
else {
toast('系统服务异常!')
}
}
catch (error) {
return Promise.reject(error)
}
}
toast 封装代码
export function toast(title: string, options?: { duration?: number, icon?: 'success' | 'loading' | 'error' | 'none' | 'fail' | 'exception', mask?: boolean }, callback?: () => void) {
const { mask = true, duration = 1000, icon = 'none' } = options || {}
if (title && title.length > 14) { // 当作字符长度>14时使用showModal展示
uni.showModal({
content: title,
showCancel: false,
success() {
if (callback && typeof callback === 'function') {
const timer = setTimeout(() => {
callback()
clearTimeout(timer)
}, duration)
}
},
})
}
else {
uni.showToast({
title,
message: title,
icon,
mask,
duration,
success() {
if (callback && typeof callback === 'function') {
const timer = setTimeout(() => {
callback()
clearTimeout(timer)
}, duration)
}
},
})
}
}
使用示例
import { type InResult, request } from '@/utils/request'
export interface IUser {
id: string,
name: string,
age?: number
}
export function getUserList(data: any): Promise<InResult<{ records: Array<IUser>, total: number }>> {
return request({
baseURL,
url: '/user/page',
method: 'POST',
params: data,
loading: true,
})
}