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(res) { // 如果后端在 Response Headers 下发新的 token,就替换本地 token tryUpdateAccessTokenFromResponseHeaders(res); const payload = res && res.data !== undefined ? res.data : null; // Most backend uses RESTfulResult wrapper if (payload && typeof payload === 'object' && typeof payload.succeeded === 'boolean') { if (payload.succeeded) resolve(payload.data); else reject(normalizeError(payload)); return; } // Fallback 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 };