wx.uploadFile 绕过全局 Token 刷新拦截器,导致上传静默失败
某个微信小程序接入了一套全局 token 刷新机制:在 wx.request 的封装层里,一旦检测到 401 就自动静默刷新登录态,用户几乎无感。然而上线后偶发图片上传失败——诡异的是,普通接口调用完全正常,上传操作却不时弹出「登录已过期」,甚至什么提示都没有,图片就是没传上去。
排查半天后才发现:问题根本不在 token 刷新逻辑本身,而在于 wx.uploadFile 根本就没走那套逻辑。
现象
- 普通 API 请求在 token 过期后能自动刷新并重试,用户无感;
- 图片/视频上传在 token 过期时直接失败,业务侧返回
err == -1(自定义「登录已过期」码); - 部分场景下上传悄然失败、无任何提示——用户以为已上传成功,实际什么都没发送;
- 偶现 HTTP 401,而同时段的普通请求没有 401。
根因
wx.uploadFile(以及 wx.downloadFile)走的是微信客户端独立的原生 HTTP 通道,与 wx.request 完全隔离。这意味着:
- 注册在
wx.request封装层上的任何拦截器(请求队列、401 处理、token 刷新、重试逻辑)对wx.uploadFile一概不生效; - 上传时的 Authorization 头需要手动构造,若构造方式与
wx.request封装层不一致(例如一个带Bearer前缀、另一个不带),后端严格解析时就会 401; - 上传封装若只写了
success回调而省略fail,网络层错误(断网、超时、服务器不可达)会导致 Promise 永远 pending,错误被整体吞掉。
坑:wx.request 拦截器对 wx.uploadFile 完全无效
项目里不管在 wx.request 封装层写了多完备的 token 刷新逻辑,上传通道都独立于外。上传封装必须自己实现 token 刷新 + 单次重试,不能依赖全局拦截器。
另外,省略 fail 回调会让所有网络层错误变成静默——这在文件上传场景下尤其危险。
正解
上传封装需要自己持有完整的「判断 token 过期 → 刷新 → 单次重试」状态机,且必须补全 fail 回调。
uploadMyFile: function (fpath, type) {
const _api = type === 'video'
? config.serverUrl + 'core/sys/upload'
: config.serverUrl + 'core/sys/uploadSafetyCheck'
const _doUpload = (isRetry) => new Promise((resolve, reject) => {
wx.uploadFile({
url: _api,
filePath: fpath,
name: 'file',
formData: {},
// 与 wx.request 封装层保持完全一致的 Bearer 前缀
header: { Authorization: 'Bearer ' + subapi.getToken() },
success(res) {
let needRetry = false
if (res.statusCode === 401) {
needRetry = true
} else {
try {
const data = JSON.parse(res.data)
if (data.err === -1) needRetry = true // 业务侧 token 过期码
} catch (_) {}
}
if (needRetry && !isRetry) {
// 刷新登录态后单次重试,不允许无限循环
subapi.login()
.then(() => _doUpload(true).then(resolve).catch(reject))
.catch(reject)
} else {
resolve(res)
}
},
fail(err) { reject(err) }, // 必须写,否则网络错误永远吞掉
})
})
return _doUpload(false)
}页面侧同样要覆盖所有已知业务码,加上兜底 else,避免静默失败:
if (data.err === 0) {
newUrls.push(data.data)
} else if (data.err === 87014) {
app.myShowToast('图片含有违规内容')
} else if (data.err === -1) {
app.myShowToast('登录已过期,请重试')
} else {
app.myShowToast('上传失败,请重试') // 兜底,绝不静默
}正解:上传封装自持 token 刷新 + 单次重试 + fail 回调三件套
- Authorization 头前缀与
wx.request封装层完全对齐(Bearer+ token); - 同时检查
res.statusCode和业务侧err码,两条路都能触发刷新; - 重试次数上限为 1,避免账号真实失效时无限循环;
- 永远提供
fail回调; - 用
grep扫全项目所有wx.uploadFile/wx.downloadFile调用点,统一补齐,不要只修一处。
一句话外卖
wx.uploadFile和wx.request走独立通道,全局拦截器对上传完全无效——上传封装必须自持 token 刷新逻辑,且任何时候都不能省略fail回调。