2026-04-04 18:57:50 +08:00

174 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { getBaseUrl } = require('./constants');
function withAuthHeader(needAuth, header) {
const app = getApp();
if (!needAuth) return header;
const token = app && app.globalData ? app.globalData.accessToken : '';
const refreshToken = app && app.globalData ? app.globalData.refreshToken : '';
if (typeof token === 'string' && token.length > 0) {
header = header || {};
header.Authorization = `Bearer ${token}`;
header['x-access-token'] = `Bearer ${token}`;
header['x-refresh-token'] = `Bearer ${refreshToken}`;
}
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));
}
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;
}
}
function request({
path,
method = 'GET',
data = {},
needAuth = false,
contentType = 'application/json',
// Internal flag: prevent infinite retry when refresh token也失败
__retry = false
}) {
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,
success: async (res) => {
// 如果后端在 Response Headers 下发新的 token,就替换本地 token
tryUpdateAccessTokenFromResponseHeaders(res);
const statusCode = res && res.data && res.data.statusCode ? res.data.statusCode : 0;
const payload = res && res.data !== undefined ? res.data : null;
// ── 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 ────────────────────────
if (payload && typeof payload === 'object' && typeof payload.succeeded === 'boolean') {
if (payload.succeeded) {
resolve(payload.data);
} else {
reject(normalizeError(payload));
}
return;
}
// ── Fallback:直接返回原始 data ───────────────────────────────
resolve(payload);
},
fail(err) {
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
}
}
reject(err);
}
});
});
}
module.exports = {
request
};