UniApp 开发微信小程序:如何优雅地实现自定义请求拦截器与响应拦截器
在使用 UniApp 开发微信小程序的过程中,网络请求是几乎每个项目都绕不开的核心环节 🌐。
无论是获取商品列表🛍️、提交订单📦,还是用户登录🔐、数据同步🔄,背后都离不开 uni.request 的调用。
然而,如果每次发起请求都要:
手动拼接 URL 🔗
添加 Token 🪪
处理 401 跳转 🚪
统一错误提示 ⚠️
……不仅代码冗余,还极易出错 😩。
这时候,请求拦截器与响应拦截器的价值就凸显出来了 ✨!
虽然 UniApp 并未像 Axios 那样内置完整的拦截器体系,但它提供了强大的扩展能力——通过 uni.addInterceptor,我们完全可以构建一套 高度封装、可维护、跨页面复用 的 HTTP 请求模块 💪。
🔍 一、为什么我们需要拦截器?
想象一下这些场景:
所有接口都以
https://api.example.com为前缀,你不想在每个页面重复写完整 URL;用户登录后,后续请求必须携带
Authorization: Bearer <token>;网络超时时间统一设为 10 秒 ⏱️;
当返回状态码为
401(未授权)时,自动清除本地登录态并跳转到登录页;接口返回
{ code: '200', msg: '成功', result: {...} },你只想拿到result部分;网络异常或业务错误时,自动弹出轻提示(Toast),无需每个页面单独处理。
如果没有拦截器,上述逻辑将散落在各个页面中,形成大量重复代码 🧩,后期维护成本极高。
而有了拦截器,这一切都可以 集中管理、一次配置、全局生效 ✅!
⚙️ 二、UniApp 拦截器机制解析
1️.请求拦截器:uni.addInterceptor
UniApp 提供了 uni.addInterceptor(type, interceptor) 方法,用于在特定 API 调用前插入自定义逻辑。
对于网络请求,我们关注的是 'request' 类型:
uni.addInterceptor('request', {
invoke(options: UniApp.RequestOptions) {
// 在这里修改 options
}
})invoke方法会在 每次调用uni.request()之前自动执行 🔄参数
options是原始请求配置对象(包含url,header,method等)直接修改
options即可(因为是引用传递,无需 return)💡
✅ 支持的类型还包括 'uploadFile'、'downloadFile',可根据需要注册。
2.响应拦截器:需自行封装
遗憾的是,UniApp 没有提供原生的“响应拦截器” ❌。
但我们可以在封装 uni.request 时,在 success 和 fail 回调中统一处理响应逻辑,从而模拟出响应拦截的效果。
这正是我们接下来要做的 🎯!
🧪 三、实战:封装一个完整的 HTTP 模块
下面是一个适用于 微信小程序 + Vue 3 + TypeScript + Pinia 项目的完整示例。
1.定义基础配置与拦截器
// utils/http.ts
import { memberStore } from '@/stores'
// 后端 API 基础地址(注意:小程序不支持代理,必须写完整 HTTPS)
const BASE_URL = 'https://example.com'
// 请求拦截器:统一处理请求前的逻辑
const requestInterceptor = {
invoke(options: UniApp.RequestOptions) {
// 1. 自动拼接 baseURL(仅当 url 不以 http 开头时)
if (!options.url.startsWith('http')) {
options.url = BASE_URL + options.url
}
// 2. 设置默认超时时间(10 秒)
options.timeout = 10000
// 3. 添加通用请求头
options.header = {
...options.header,
'source-client': 'miniapp', // 标识来源为小程序
}
// 4. 注入用户 Token(如果已登录)
const token = memberStore().profile?.token
if (token) {
options.header.Authorization = token
}
},
}
// 注册拦截器(同时覆盖普通请求和文件上传)
uni.addInterceptor('request', requestInterceptor)
uni.addInterceptor('uploadFile', requestInterceptor)2.封装核心请求函数(含响应处理)
// 定义后端返回的数据结构
export interface HttpResult<T> {
code: string // 业务状态码,如 '200'
msg: string // 提示信息
result: T // 实际数据
}
// 封装 uni.request 为 Promise,并统一处理响应
export const http = <T>(options: UniApp.RequestOptions): Promise<HttpResult<T>> => {
return new Promise((resolve, reject) => {
uni.request({
...options,
success(res) {
// HTTP 状态码 2xx 表示请求成功到达服务器
if (res.statusCode >= 200 && res.statusCode < 300) {
// 解析业务数据
resolve(res.data as HttpResult<T>)
}
// 特殊处理 401:Token 失效,跳转登录
else if (res.statusCode === 401) {
const store = memberStore()
store.clearProfile() // 清除本地用户信息
uni.navigateTo({ url: '/pages/login/login' })
reject(res)
}
// 其他业务错误(如 code !== '200')
else {
const errMsg = (res.data as HttpResult<unknown>)?.msg || '请求失败'
uni.showToast({ icon: 'none', title: errMsg })
reject(res)
}
},
fail(err) {
// 网络层错误(如 DNS 解析失败、超时)
uni.showToast({ icon: 'none', title: '网络错误,请检查网络连接' })
reject(err)
},
})
})
}3.在页面中使用
<!-- pages/index/index.vue -->
<script setup lang="ts">
import { http } from '@/utils/http'
onLoad(async () => {
try {
// 发起请求,泛型指定 result 类型
const res = await http<{ nickname: string; avatar: string }>('/member/profile')
console.log('用户昵称:', res.result.nickname) 👤
} catch (error) {
// 错误已在拦截器中处理,此处可选择性记录日志
console.error('请求异常:', error) 🐞
}
})
</script>⚠️四、关键注意事项
拦截器只对 uni.request 生效
如果你在项目中引入了
axios或使用fetch,它们 不会触发uni.addInterceptor。因此,所有网络请求应统一走
http函数,确保逻辑一致性 🧵。
🎉 五、总结
通过 uni.addInterceptor + 自定义 Promise 封装,我们成功在 UniApp 小程序中实现了:
✅ 请求拦截:统一处理 URL、Header、Token、超时
✅ 响应拦截:集中解析数据、处理 401、错误提示
✅ 类型安全:TypeScript 泛型支持,开发体验更佳 🧠
✅ 多端兼容:H5 可用代理,小程序直连后端
这套方案已在多个上线项目中验证,稳定可靠 💯。它不仅减少了重复代码,还极大提升了错误处理的一致性和用户体验。
希望这篇文章能帮助你在 UniApp 开发中写出更优雅、更专业的网络层代码 🧑💻!
如果你有任何疑问或优化建议,欢迎在评论区交流 💬~
希望这篇文章能帮助你在 UniApp 开发中写出更优雅、更专业的网络层代码 🧑💻!
如果你有任何疑问或优化建议,欢迎在评论区交流 💬~
📌 延伸阅读: