必须手写 request 封装:统一 URL 补全(H5 相对路径、其他端完整 HTTPS)、同步读取并正确合并 token 到 header、解析 data.code 判定业务状态、401 抛标准错误对象、并发刷新 token 需加锁。

直接封装 uni.request 是必须的,靠 uni.addInterceptor 在 App 端基本无效——iOS/Android 原生容器绕过 JS 拦截器,H5 和小程序能用但不可跨端统一。真正稳的方案是手写一个 request 函数,把 URL 补全、token 注入、错误分类这三件事收口到一处。
不同端 URL 拼接必须条件编译
H5 能用相对路径(配合 webpack 代理),小程序和 APP-PLUS 必须是完整 HTTPS 地址;硬拼 baseUrl + url 会导致 H5 请求发错域名、小程序直接 404。
- 用
#ifdef H5/#ifndef H5区分逻辑:isH5 ? '/api' + options.url : config.baseUrl + options.url - 不要在封装里写死
https://开头的baseUrl,改用环境变量注入(如process.env.VUE_APP_API_BASE) - URL 若已含
?查询参数,别手动拼,交给uni.request自动处理,否则 H5 代理可能失效
Token 注入顺序和读取时机很关键
常见问题不是没 token,而是 header 合并时被覆盖,或读取时 store 还没初始化,uni.getStorageSync('token') 返回空字符串。
- header 必须用展开运算符合并:
{ 'Content-Type': 'application/json', ...options.header, 'Authorization': token },否则options.header会覆盖默认项 - token 必须同步读取:
const token = uni.getStorageSync('token') || '',别依赖 Pinia/Vuex 异步状态(setup 顶层调用时 store 可能未 ready) - 响应头若返回新 token(如
res.header.Authorization),需立刻uni.setStorageSync('token', ...),否则下个请求仍用旧值 - 小程序端 header 键名大小写敏感:
authorization无效,必须是Authorization
401 错误不能在封装层跳转
uni.redirectTo 在非页面上下文(如 utils/request.js)中不可用,且 Promise 链中 throw 后未被 catch 会静默失败。
- 封装层只做一件事:抛出标准化错误对象,例如
{ code: 401, message: '登录过期' } - 页面层用
try/catch或.catch()接收,在onShow或业务入口统一处理跳转 - Vue 3 Composition API 下,避免在
setup顶层执行请求;应在onMounted中调用并处理 401
别只看 statusCode,业务 code 才是真实状态
绝大多数后端返回 HTTP 200,靠响应体 res.data.code 判定成功与否。如果封装层只检查 res.statusCode === 200 就 resolve,等于把权限不足、参数错误、登录过期全当成功。
- 必须解析
res.data,检查code字段(常见0表示成功,1001表示 token 过期) - 对
code !== 0的情况统一reject,让业务层决定是重试、上报还是静默忽略 - 别在封装里调
uni.showToast,那是业务逻辑;封装只负责分类错误,不负责提示方式
最易被忽略的是并发刷新 token 场景:多个请求同时 401,若都触发 refreshToken(),后端可能返回 429 或状态错乱。必须加锁,用一个 refreshingPromise 缓存首次刷新结果,后续请求 await 同一个 Promise。


















