| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- import { API_BASE_URL, REQUEST_TIMEOUT_MS } from './config.js'
- import { forceRelogin } from './auth-session.js'
- /** 这些接口的业务 code=401 表示凭据错误等,不是会话过期 */
- const AUTH_EXEMPT_PATHS = [
- '/web/auth/login',
- '/web/auth/login-rsa-public-key',
- '/web/auth/refresh-token',
- '/mini/auth/wechat-login'
- ]
- function isAuthExemptPath(path) {
- const p = String(path || '')
- for (let i = 0; i < AUTH_EXEMPT_PATHS.length; i++) {
- if (p.indexOf(AUTH_EXEMPT_PATHS[i]) !== -1) return true
- }
- return false
- }
- function hasBearerToken(header) {
- const auth = (header && header.Authorization) || ''
- return /^Bearer\s+\S+/i.test(String(auth))
- }
- /**
- * HTTP 401,或已带 token 的业务 code=401 → 强制重新登录。
- * 登录接口豁免,避免「账号密码错误」被当成会话失效。
- */
- function maybeForceRelogin(options, httpStatus, body) {
- if (isAuthExemptPath(options && options.url)) return
- const bizCode = body && body.code
- const msg =
- (body && typeof body.message === 'string' && body.message.trim()) ||
- (body && typeof body.detail === 'string' && body.detail.trim()) ||
- ''
- if (httpStatus === 401 || (bizCode === 401 && hasBearerToken(options && options.header))) {
- forceRelogin(msg || '登录已失效,请重新登录')
- }
- }
- /**
- * 解析后端统一响应。成功返回 data;失败抛 Error(message 为业务文案)。
- * @param {{ code?: number, message?: string, data?: any }} body
- * @param {number} [httpStatus]
- */
- export function unwrapResponse(body, httpStatus) {
- if (body == null || typeof body !== 'object') {
- const err = new Error('网络异常,请稍后重试')
- err.code = 'BAD_RESPONSE'
- throw err
- }
- const code = body.code
- if (code === 200) {
- return body.data
- }
- const msg =
- (typeof body.message === 'string' && body.message.trim()) ||
- '请求失败'
- const err = new Error(msg)
- err.code = code
- err.httpStatus = httpStatus
- throw err
- }
- function joinUrl(path) {
- const base = (API_BASE_URL || '').replace(/\/$/, '')
- const p = path.startsWith('/') ? path : `/${path}`
- return `${base}${p}`
- }
- /**
- * @param {{ url: string, method?: string, data?: any, header?: Record<string, string> }} options
- * @returns {Promise<any>} data 字段
- */
- export function request(options) {
- const method = (options.method || 'GET').toUpperCase()
- const url = joinUrl(options.url)
- const header = Object.assign(
- { 'Content-Type': 'application/json' },
- options.header || {}
- )
- const reqOpts = { url: options.url, header }
- return new Promise((resolve, reject) => {
- uni.request({
- url,
- method,
- data: options.data,
- header,
- timeout: REQUEST_TIMEOUT_MS,
- success(res) {
- try {
- const body = res.data
- maybeForceRelogin(reqOpts, res.statusCode, body)
- if (res.statusCode >= 200 && res.statusCode < 300) {
- resolve(unwrapResponse(body, res.statusCode))
- return
- }
- // HTTP 非 2xx:若 body 仍是业务包,优先展示 message
- try {
- resolve(unwrapResponse(body, res.statusCode))
- } catch (bizErr) {
- reject(bizErr)
- }
- } catch (e) {
- reject(e)
- }
- },
- fail() {
- const err = new Error('网络异常,请稍后重试')
- err.code = 'NETWORK'
- reject(err)
- }
- })
- })
- }
- export function get(url, header) {
- return request({ url, method: 'GET', header })
- }
- export function post(url, data, header) {
- return request({ url, method: 'POST', data, header })
- }
|