uni-app 不能只靠 statusCode === 401 判断登录失效,因后端常返回 200 + code: 401 的业务错误,且网关可能将 401 转为 200,导致静默失败;应统一响应拦截、优先解析 data 并按约定字段(如 code)判断。

uni-app 为什么不能只靠 statusCode === 401 判断登录失效
后端返回 200 是常态,真正决定业务是否成功的往往是响应体里的 code 字段(比如 { code: 401, msg: "登录已过期" })。如果只检查 HTTP 状态码,会漏掉大量业务层错误;更危险的是,某些网关或代理可能把 401 自动转成 200 + 错误体,导致页面静默失败。
统一处理的关键是:**所有请求必须走同一套响应拦截逻辑,且优先解析 res.data,再按约定字段(如 code、success、errCode)做分支判断。**
- 不要在每个页面里写
if (res.data.code === 401),而是把校验逻辑收进封装的request函数 - 区分三类错误:网络异常(
err.errMsg含"timeout"或"Network Error")、HTTP 错误(res.statusCode !== 200)、业务错误(res.data.code !== 0 || res.data.success === false) - 对
code: 401类错误,必须加刷新锁(见下一条),避免并发触发多次 token 刷新
如何用 refreshingPromise 防止多个请求同时刷新 token
当首页、用户中心、订单列表三个接口几乎同时返回 code: 401,若每个都调 refreshToken(),就会发三次刷新请求——后端很可能拒绝第二次起的请求,或返回不一致的 token,造成后续请求持续 401。
正确做法是用一个缓存 Promise 控制并发:
let refreshingPromise = null
// 在响应拦截器中
if (res.data?.code === 401) {
if (!refreshingPromise) {
refreshingPromise = refreshToken()
}
const newToken = await refreshingPromise
// 更新本地 token
uni.setStorageSync('token', newToken)
// 清空锁,允许下次刷新
refreshingPromise = null
// 重发原请求(需保留原始 config)
return request(originalConfig)
}
-
refreshingPromise必须是模块级变量,不能放在函数内或每次新建 - 刷新失败时一定要清空
refreshingPromise = null,否则后续所有 401 请求都会卡死在await - 重发请求前,记得更新
config.header.Authorization,否则还是带着旧 token 发
小程序 / App / H5 三端错误码映射要分开维护
云函数调用、uniCloud.callFunction 返回的 errCode 和 HTTP 接口的 code 不是一套体系;App 端原生 SDK(如微信登录、地图)抛出的错误码又来自 5+ API 规范(如 code: -6 表示网络错误)。混在一起处理必然漏判。
建议分层定义错误码表:
- HTTP 接口:集中维护
httpErrorCodeMap = { 401: '登录失效', 500: '服务异常' } - 云函数:单独建
cloudErrorCodeMap = { 401: 'login_required', 500: 'cloud_function_error' } - 5+ API:用官方文档的
-6、-1001等值,直接比对err.code - 所有错误最终都转成统一结构:
{ type: 'business', code: 401, message: '请重新登录' },供上层 toast 或日志使用
error handler 里别直接 uni.navigateTo 跳登录页
在响应拦截器或全局错误处理中写 uni.navigateTo({ url: '/pages/login/login' }) 看似合理,但会破坏调用链:原请求的 .then() 仍会执行,可能导致空数据渲染、重复提交、状态错乱。
更可控的方式是让错误穿透到业务层:
- 对 401 这类需强干预的错误,
return Promise.reject(new Error('auth_expired')) - 页面中用
try/catch或.catch()显式处理,再决定跳转、弹窗或静默重试 - 只有在兜底场景(如 App 端未 catch 的 Promise 拒绝)才用
uni.onUnhandledRejection做最后跳转 - 避免在工具函数里耦合路由跳转逻辑,否则单元测试和 mock 几乎无法进行
真正难的不是识别错误码,而是让错误流经的每一层都清楚自己该做什么、不该做什么——拦截器负责转换,业务层负责决策,全局监听只做最后防线。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











