finai/miniapp/utils/request.js

144 lines
3.9 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 app = getApp()
function apiUrl(path) {
const base = (app && app.globalData && app.globalData.apiBase) || 'http://localhost:8000'
return base.replace(/\/$/, '') + path
}
function authHeader() {
const token = (app && app.globalData && app.globalData.token) || wx.getStorageSync('token')
return token ? { Authorization: 'Bearer ' + token } : {}
}
function request(path, options) {
options = options || {}
return new Promise((resolve, reject) => {
wx.request({
url: apiUrl(path),
method: options.method || 'GET',
data: options.data,
header: Object.assign(
{ 'Content-Type': 'application/json' },
authHeader(),
options.header || {}
),
success(res) {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data)
} else {
const detail = (res.data && (res.data.detail || res.data.message)) || res.errMsg || '请求失败'
reject(new Error(typeof detail === 'string' ? detail : JSON.stringify(detail)))
}
},
fail(err) {
reject(new Error(err.errMsg || '网络异常'))
}
})
})
}
function uploadFile(path, filePath, name) {
return new Promise((resolve, reject) => {
wx.uploadFile({
url: apiUrl(path),
filePath: filePath,
name: name || 'file',
header: authHeader(),
success(res) {
if (res.statusCode >= 200 && res.statusCode < 300) {
try {
resolve(JSON.parse(res.data))
} catch (e) {
resolve(res.data)
}
} else {
reject(new Error(res.data || '上传失败'))
}
},
fail(err) {
reject(new Error(err.errMsg || '上传失败'))
}
})
})
}
/**
* 以分块方式请求 SSE 流。onEvent({event, data}) 回调每个事件。
* 返回一个 requestTask可用于中断。
*/
function streamRequest(path, body, onEvent, onDone, onError) {
const task = wx.request({
url: apiUrl(path),
method: 'POST',
data: body,
responseType: 'text',
enableChunked: true,
header: Object.assign(
{ 'Content-Type': 'application/json', Accept: 'text/event-stream' },
authHeader()
),
success(res) {
if (res.statusCode < 200 || res.statusCode >= 300) {
onError && onError(new Error('HTTP ' + res.statusCode + ' ' + (res.data || '')))
} else {
onDone && onDone()
}
},
fail(err) {
onError && onError(new Error(err.errMsg || '流式请求失败'))
}
})
let buf = ''
const parseChunk = (chunk) => {
buf += chunk
// SSE 事件以空行分隔(\n\n
let idx
while ((idx = buf.indexOf('\n\n')) !== -1) {
const raw = buf.slice(0, idx)
buf = buf.slice(idx + 2)
let event = 'message'
let dataStr = ''
raw.split('\n').forEach((line) => {
if (line.startsWith('event:')) event = line.slice(6).trim()
else if (line.startsWith('data:')) dataStr += line.slice(5).trim()
})
let data
try { data = JSON.parse(dataStr) } catch (e) { data = dataStr }
onEvent && onEvent({ event: event, data: data })
}
}
if (task && task.onChunkReceived) {
task.onChunkReceived((response) => {
const buffer = response.data
let text = ''
if (typeof buffer === 'string') {
text = buffer
} else {
// ArrayBuffer -> UTF-8
const bytes = new Uint8Array(buffer)
// 简易 UTF-8 解码
try {
if (typeof TextDecoder !== 'undefined') {
text = new TextDecoder('utf-8').decode(bytes)
} else {
text = decodeURIComponent(escape(String.fromCharCode.apply(null, bytes)))
}
} catch (e) {
text = String.fromCharCode.apply(null, bytes)
}
}
parseChunk(text)
})
}
return task
}
module.exports = {
apiUrl: apiUrl,
request: request,
uploadFile: uploadFile,
streamRequest: streamRequest
}