174 lines
6.4 KiB
JavaScript
Raw Normal View History

2026-03-30 18:08:27 +08:00
const { getBaseUrl } = require('./constants');
function withAuthHeader(needAuth, header) {
const app = getApp();
if (!needAuth) return header;
const token = app && app.globalData ? app.globalData.accessToken : '';
2026-04-03 17:57:08 +08:00
const refreshToken = app && app.globalData ? app.globalData.refreshToken : '';
2026-03-30 18:08:27 +08:00
if (typeof token === 'string' && token.length > 0) {
header = header || {};
header.Authorization = `Bearer ${token}`;
2026-04-03 17:57:08 +08:00
header['x-access-token'] = `Bearer ${token}`;
header['x-refresh-token'] = `Bearer ${refreshToken}`;
2026-03-30 18:08:27 +08:00
}
return header;
}
function normalizeError(payload) {
if (!payload) return new Error('Empty response');
// RESTfulResult usually has `errors` and/or `message`
if (typeof payload === 'string') return new Error(payload);
if (payload.errors) return new Error(payload.errors.toString());
if (payload.message) return new Error(payload.message);
return new Error(JSON.stringify(payload));
}
2026-04-03 17:57:08 +08:00
function getHeaderValue(headers, headerName) {
if (!headers || typeof headers !== 'object') return '';
const target = String(headerName || '').toLowerCase();
for (const k in headers) {
if (Object.prototype.hasOwnProperty.call(headers, k) && String(k).toLowerCase() === target) {
const v = headers[k];
return typeof v === 'string' ? v : '';
}
}
return '';
}
function tryUpdateAccessTokenFromResponseHeaders(res) {
// wx.request 的返回头在 res.header(部分环境也可能是 res.headers)
const headers = (res && res.header) || (res && res.headers) || null;
const newAccessToken = getHeaderValue(headers, 'x-access-token');
if (!newAccessToken) return;
const app = getApp();
if (app && typeof app.setAuth === 'function') {
app.setAuth({ accessToken: newAccessToken });
} else {
wx.setStorageSync('accessToken', newAccessToken);
if (app && app.globalData) app.globalData.accessToken = newAccessToken;
}
}
2026-03-30 18:08:27 +08:00
function request({
path,
method = 'GET',
data = {},
needAuth = false,
2026-04-03 17:57:08 +08:00
contentType = 'application/json',
// Internal flag: prevent infinite retry when refresh token也失败
__retry = false
2026-03-30 18:08:27 +08:00
}) {
const baseUrl = getBaseUrl();
const url = `${baseUrl}${path}`;
const header = withAuthHeader(needAuth, {
'content-type': contentType
});
return new Promise((resolve, reject) => {
wx.request({
url,
method: method.toUpperCase(),
data: method.toUpperCase() === 'GET' ? data : data,
header: header,
2026-04-04 18:57:50 +08:00
success: async (res) => {
2026-04-03 17:57:08 +08:00
// 如果后端在 Response Headers 下发新的 token,就替换本地 token
tryUpdateAccessTokenFromResponseHeaders(res);
2026-04-04 18:57:50 +08:00
const statusCode = res && res.data && res.data.statusCode ? res.data.statusCode : 0;
2026-03-30 18:08:27 +08:00
const payload = res && res.data !== undefined ? res.data : null;
2026-04-04 18:57:50 +08:00
// ── 401 未授权 ──────────────────────────────────────────────
if (statusCode === 401) {
wx.clearStorageSync();
const app = getApp();
if (app && app.globalData) {
app.globalData.accessToken = '';
app.globalData.refreshToken = '';
}
wx.showModal({
title: '登录失效',
content: '您的登录已失效,请重新登录。',
showCancel: false,
complete() {
wx.navigateTo({ url: '/subpackages/mine/login/login' });
}
});
reject(new Error('Unauthorized'));
return;
}
// ── 403 禁止访问 ─────────────────────────────────────────────
if (statusCode === 403) {
wx.showToast({ title: '无访问权限', icon: 'none' });
reject(new Error('Forbidden'));
return;
}
// ── 404 资源不存在 ───────────────────────────────────────────
if (statusCode === 404) {
wx.showToast({ title: '请求资源不存在', icon: 'none' });
reject(new Error('Not Found'));
return;
}
// ── 5xx 服务端错误 ───────────────────────────────────────────
if (statusCode >= 500) {
wx.showToast({ title: '服务器异常,请稍后重试', icon: 'none' });
reject(new Error(`Server Error: ${statusCode}`));
return;
}
// ── 其他非 2xx ───────────────────────────────────────────────
if (statusCode !== 0 && (statusCode < 200 || statusCode >= 300)) {
wx.showToast({ title: `请求失败(${statusCode})`, icon: 'none' });
reject(new Error(`HTTP Error: ${statusCode}`));
return;
}
// ── 2xx 成功:解析业务层 RESTfulResult ────────────────────────
2026-03-30 18:08:27 +08:00
if (payload && typeof payload === 'object' && typeof payload.succeeded === 'boolean') {
2026-04-04 18:57:50 +08:00
if (payload.succeeded) {
resolve(payload.data);
} else {
reject(normalizeError(payload));
}
2026-03-30 18:08:27 +08:00
return;
}
2026-04-04 18:57:50 +08:00
// ── Fallback:直接返回原始 data ───────────────────────────────
2026-03-30 18:08:27 +08:00
resolve(payload);
},
fail(err) {
2026-04-03 17:57:08 +08:00
if (err && err.statusCode === 401 && needAuth && !__retry) {
// 401: try refresh token, then retry the original request once.
const originalErr = err;
try {
const auth = require('./auth');
if (auth && typeof auth.refreshAccessToken === 'function') {
auth
.refreshAccessToken()
.then(() => {
return request({ path, method, data, needAuth, contentType, __retry: true });
})
.then(resolve)
.catch(() => {
reject(originalErr);
});
return;
}
} catch (e) {
// ignore and fall through to rejecting original error
}
}
2026-03-30 18:08:27 +08:00
reject(err);
}
});
});
}
module.exports = {
request
};