feat(mobile-h5): add thin uni.request wrapper for API calls
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
25d1b3454b
commit
2bc7f353f0
2 files changed
+105
No files matched your search
@@ -0,0 +1,83 @@
|
||||
import { API_BASE_URL, REQUEST_TIMEOUT_MS } from './config.js'
|
||||
|
||||
/**
|
||||
* 解析后端统一响应。成功返回 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 || {}
|
||||
)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url,
|
||||
method,
|
||||
data: options.data,
|
||||
header,
|
||||
timeout: REQUEST_TIMEOUT_MS,
|
||||
success(res) {
|
||||
try {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(unwrapResponse(res.data, res.statusCode))
|
||||
return
|
||||
}
|
||||
// HTTP 非 2xx:若 body 仍是业务包,优先展示 message
|
||||
try {
|
||||
resolve(unwrapResponse(res.data, 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 })
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { unwrapResponse } from './request.js'
|
||||
|
||||
test('unwrapResponse returns data when code is 200', () => {
|
||||
const data = unwrapResponse({ code: 200, message: 'success', data: { token: 't' } })
|
||||
assert.deepEqual(data, { token: 't' })
|
||||
})
|
||||
|
||||
test('unwrapResponse throws with backend message when code is not 200', () => {
|
||||
assert.throws(
|
||||
() => unwrapResponse({ code: 401, message: '用户名或密码错误', data: null }),
|
||||
(e) => e instanceof Error && e.message === '用户名或密码错误' && e.code === 401
|
||||
)
|
||||
})
|
||||
|
||||
test('unwrapResponse throws network message on null body', () => {
|
||||
assert.throws(
|
||||
() => unwrapResponse(null),
|
||||
(e) => e instanceof Error && e.message === '网络异常,请稍后重试'
|
||||
)
|
||||
})
|
||||
Reference in new issue
Block a user