uni.addInterceptor 是 uni-app 的原生请求拦截 API,仅 H5 和微信 / 支付宝等小程序支持,App 端(尤其 iOS)不生效,需手动封装 uni.request;token 注入需 Promise 化处理时序问题;401 应加刷新锁防并发;须分网络层与业务层双校验错误;跨端错误需统一包装。
uni.addInterceptor 是什么,哪些端能用?
uni.addinterceptor 是 uni-app 提供的原生拦截注册 api,但它 ** 不是全端可用 **——仅 h5 和微信 / 支付宝等主流小程序支持;app 端(尤其是 ios)基本不生效,uni.request 调用完全绕过它。很多开发者在 app 上写了拦截器却没反应,就是栽在这儿。
实际使用时得按端判断:
- H5 / 小程序:可放心用
uni.addInterceptor('request', {……})做前置 URL 拼接、header 注入 - App 端:必须手动封装
uni.request,不能依赖拦截器 API - 所有端统一行为:建议「以手动封装为主,
uni.addInterceptor为辅」,避免逻辑分裂
token 自动注入为什么不能只靠同步读取?
因为 token 往往存在异步获取场景:比如登录后存进 uni.setStorageSync,但某些模块(如首页请求)可能在 store 初始化前就发请求,uni.getStorageSync('token') 拿到的是空值,导致 401 —— 这不是代码写错了,是时序问题。
更稳妥的做法是:
- 封装层加一层 Promise 化 token 获取逻辑(例如从 Pinia store 异步读,或 fallback 到 storage 同步读)
- 对敏感接口(如用户中心)做 token 存在校验,缺失时主动 reject 并跳登录页,而不是静默发一个无 token 请求
- 避免在拦截器里直接
uni.redirectTo,应把控制权交还业务层,否则测试和 mock 极难
401 错误处理最容易漏掉的并发刷新问题
当多个请求几乎同时收到 401,如果每个都触发 token 刷新流程,就会发多次刷新请求,后端可能拒绝或返回混乱状态。这不是理论风险,是真实线上高频问题。
正确做法是加一层「刷新锁」:
- 用一个
refreshingPromise变量缓存正在执行的刷新请求 Promise - 后续 401 请求先 await 它,复用结果,而不是各自重试
- 刷新失败要清空该 Promise,否则后续请求永远卡住
示例关键逻辑:
let refreshingPromise = null;<br>if (res.statusCode === 401) {<br> if (!refreshingPromise) {<br> refreshingPromise = refreshToken();<br> }<br> await refreshingPromise;<br> refreshingPromise = null;<br> // 重发原请求 <br>}
错误码统一处理别只看 statusCode
后端返回 200 不代表业务成功,常见结构是 {code: 1001, msg: 'token 已过期', data: null}。只判 statusCode === 200 就 resolve,等于把业务错误当成功处理了。
应该分两层判断:
- 网络层:检查
res.errMsg(如"request:fail timeout")、res.statusCode是否在 200–299 范围 - 业务层:再解析
res.data,根据约定字段(如code !== 0或success === false)走错误分支 - 不同错误类型分流处理:401 跳登录,1001 提示并清理本地态,其他弹
msg即可
真正麻烦的是跨端错误信息不一致:小程序 fail 回调里 err 是字符串,H5 是 Error 对象,App 端还可能带 platform 字段——统一包装成标准错误对象,比硬扛各端差异更可持续。
实际项目里最常被忽略的,是 token 刷新后的请求重放机制和 loading 状态的嵌套控制。前者一漏就白刷 token,后者一错就出现“关了 loading 又弹新 loading”的体验断层。