@xiao-ying/miniapp-request
v1.1.0
Published
Axios-like request wrapper built on @xiao-ying/miniapp-sdk with middleware and shared headers
Readme
@xiao-ying/miniapp-request
基于 @xiao-ying/miniapp-sdk 的 axios 风格请求层,统一封装 request / uploadFile / downloadFile,支持中间件、异步/静态共享请求头,以及可配置的 HTTP 状态码抛错策略。
- SDK 行为:
xy.request等始终返回{ data, statusCode, headers },不因 4xx/5xx 抛错。 - 本封装默认行为:
statusCode < 400视为成功,否则抛出HTTP_ERROR_xxx;可通过validateStatusCode改为不抛或自定义规则。
安装
pnpm add @xiao-ying/miniapp-request快速开始
import { createXyRequestClient } from '@xiao-ying/miniapp-request'
const client = createXyRequestClient({
baseURL: 'https://api.example.com',
// 共享请求头可异步获取
headers: async ({ action }) => ({
Authorization: `Bearer ${await getToken()}`,
'X-Action': action
})
})
// 中间件:打印耗时
client.use(async (ctx, next) => {
const startedAt = Date.now()
const result = await next()
console.info('[xy-request]', ctx.request.url, result.kind, `${Date.now() - startedAt}ms`)
return result
})
// 请求(GET / POST / PUT / PATCH / DELETE)
const res = await client.post('/foo', { hello: 'world' }, { params: { lang: 'zh-CN' } })
console.log(res.data)
// 上传
await client.uploadFile({
url: '/upload',
method: 'POST',
fileKey: 'file',
filePath: '/tmp/demo.jpg'
})
// 下载
const download = await client.downloadFile({ url: '/file.zip' })
console.log(download.filePath)API 总览
- 工厂:
createXyRequestClient<TExtra extends Record<string, any> = Record<string, any>>(options?: RequestClientOptions<TExtra>): XyRequestClient<TExtra> - 实例方法:
use(middleware):注册中间件(链式)setSharedHeaders(source):更新共享请求头(静态或异步函数)request(config),以及get / delete / post / put / patchuploadFile(config)、downloadFile(config)extend(partialOptions):继承当前配置,中间件可追加
- 关键选项:
baseURL:统一前缀(仅对相对路径生效)headers:共享请求头(对象或函数)middlewares:默认中间件数组validateStatusCode?: (code: number) => boolean:返回 true 视为成功,不抛错;默认code < 400
共享请求头
- 支持静态对象:
{ Authorization: 'Bearer xxx' } - 支持异步函数:
({ action, url }) => Promise<Headers> - 注入顺序:共享头 -> 客户端中间件 -> 局部中间件 -> 最终请求
- 在任意中间件中可调用
ctx.mergeHeaders()追加/覆盖
client.use 用法示例
中间件签名:
(ctx, next) => Promise<RequestResult>,其中ctx.request已包含kind/url/headers/...,以及可选的extra元数据,next()进入后续中间件或最终请求。
请求/响应日志
client.use(async (ctx, next) => {
console.debug('➡️', ctx.request.method ?? 'GET', ctx.request.url)
const res = await next()
console.debug('⬅️', res.kind, res.response.statusCode)
return res
})动态鉴权头(可与共享头并用)
client.use(async (ctx, next) => {
const token = await getTokenMaybeRefresh()
if (token) ctx.mergeHeaders({ Authorization: `Bearer ${token}` })
return next()
})错误提示:链内不抛、链外抛(默认)
默认 validateStatusCode = (c) => c < 400。await next() 不会因 4xx/5xx 抛错,方便在中间件里直接读取响应做 toast;中间件链结束后,client.post() 等会按校验结果在返回前抛出 HTTP_ERROR_xxx:
client.use(async (ctx, next) => {
const res = await next() // 不需要 try/catch
if (res.response.statusCode >= 400) {
xy.showToast({ title: '请求失败', subtitle: res.response.data?.message, type: 'error' })
}
return res
})
// 调用处依然会抛出 HTTP_ERROR_xxx
await client.post('/foo', data) // 4xx/5xx 将在这里抛出错误提示:完全不抛
创建客户端时放宽校验,4xx/5xx 既不会在 next() 抛,也不会在 client.post() 抛:
const client = createXyRequestClient({
validateStatusCode: () => true // 永远不抛错
})
client.use(async (ctx, next) => {
const res = await next()
if (res.response.statusCode >= 400) {
xy.showToast({ title: '请求失败', subtitle: res.response.data?.message, type: 'error' })
}
return res
})短路 / Mock(不触网)
client.use(async (ctx, next) => {
if (ctx.request.url.startsWith('/mock/')) {
return { kind: 'request', response: { data: { ok: true }, statusCode: 200, headers: {} } }
}
return next()
})局部中间件
client.post('/foo', data, {
middlewares: [async (ctx, next) => {
ctx.mergeHeaders({ 'X-Trace': crypto.randomUUID() })
return next()
}]
})extra 元数据(可类型化)
extra 仅用于中间件链路的上下文传递,不会透传到底层请求。
const client = createXyRequestClient<{ traceId: string }>()
client.use(async (ctx, next) => {
console.log(ctx.request.extra?.traceId)
return next()
})实例复用
const authed = client.extend({
headers: { 'X-Env': 'prod' },
validateStatusCode: (code) => code < 500 // 允许 4xx 走正常分支
})与全局 xy 实例
默认使用全局 xy(由宿主或 @xiao-ying/miniapp-sdk 初始化)。浏览器环境请同时引入 @xiao-ying/miniapp-sdk-browser 以完成注入。如需自定义 SDK 实例,可在 createXyRequestClient({ sdk }) 或 client.extend({ sdk }) 中显式传入。
WebSocket
miniapp-request 提供 XyWsClient 作为 xy.ws 的同步封装,支持中间件链与多层代理嵌套。
创建客户端
import { createXyWsClient } from '@xiao-ying/miniapp-request'
const wsClient = createXyWsClient({
baseURL: 'https://ws.example.com'
})
const ws = wsClient.connect({ url: '/socket' })
ws.onOpen = () => console.info('open')
ws.onMessage = (evt) => console.info('message', evt.data)多层代理(中间件)
import { createXyWsClient } from '@xiao-ying/miniapp-request'
import {
createWsProxyMiddleware,
createWsPathUrlBuilder
} from '@xiao-ying/miniapp-request-proxy'
const wsClient = createXyWsClient()
wsClient.use(createWsProxyMiddleware({
buildUrl: createWsPathUrlBuilder({ prefix: 'https://inner-proxy.com/ws' })
}))
wsClient.use(createWsProxyMiddleware({
buildUrl: createWsPathUrlBuilder({ prefix: import.meta.env.VITE_XY_PROXY_PREFIX ?? '/dev-proxy/' })
}))
const ws = wsClient.connect({ url: 'wss://ws.example.com/echo' })中间件观察示例
wsClient.use((ctx, next) => {
const socket = next()
const originalSend = socket.send.bind(socket)
socket.send = (data) => {
console.info('[ws send]', data)
originalSend(data)
}
socket.addEventListener('message', (evt) => {
console.info('[ws message]', evt.data)
})
return socket
})说明:
connect与中间件均为同步调用,若需异步 token 请提前获取后再连接。- 代理仅改写 URL,不注入请求头。
- WS 代理中间件来自
@xiao-ying/miniapp-request-proxy。
