@zero-bits/taro-request
v1.1.0
Published
Zero-Bits Taro Request Library
Readme
@zero-bits/taro-request
适用于 Taro 的请求库,提供两套方案:
- OnionRequest — 基于洋葱模型的原生请求方案,直接调用
Taro.request,内置 Token 注入、无感刷新、业务异常拦截等中间件,零外部依赖 - request / initializeRequest — 基于
@zero-bits/request(Axios)的方案,通过taroAdapter将 Axios 的请求转换为Taro.request,适合已有 Axios 使用习惯的团队
安装
# npm
npm install @zero-bits/taro-request axios
# pnpm
pnpm add @zero-bits/taro-request axios
@tarojs/taro、axios、react为 peerDependency,需在宿主项目中自行安装。
快速上手
方案一:OnionRequest(推荐)
import { createRequest, setupTaroEnvironment } from '@zero-bits/taro-request'
// 小程序环境需要在入口执行一次,补齐缺失的原生对象(如 AbortController)
setupTaroEnvironment()
const http = createRequest({
baseURL: 'https://api.example.com',
timeout: 15000,
getAccessToken: () => getStoredToken(),
isSuccess: (data) => data.code === 0,
resolveData: (data) => data.result,
onBizError: (err) => showToast(err.msg),
onHttpError: (statusCode, err) => console.error(statusCode, err),
})
// GET
const user = await http.get<User>('/user/info')
// POST
const res = await http.post<LoginRes>('/auth/login', { username: 'admin', password: '123456' })
// 文件上传
const uploadRes = await http.upload('/file/upload', tempFilePath, { type: 'image' })方案二:request / initializeRequest(Axios 方案)
import { initializeRequest, request, taroAdapter, defaultTransformRequest } from '@zero-bits/taro-request'
initializeRequest({
baseURL: 'https://api.example.com',
timeout: 10000,
adapter: taroAdapter,
transformRequest: [defaultTransformRequest],
})
const user = await request.get<User>('/user/info')OnionRequest
createRequest(config)
工厂函数,创建一个内置中间件链的 OnionRequest 实例。
内置中间件执行顺序:
requestInterceptors(用户前置)
→ logMiddleware(耗时日志)
→ authMiddleware(Token 注入 + 无感刷新)
→ bizMiddleware(HTTP 状态码 + 业务异常 + 数据提纯)
→ responseInterceptors(用户后置)
→ 真实网络请求 (Taro.request)RequestConfig
| 属性 | 类型 | 说明 |
|------|------|------|
| baseURL | string | 接口根地址前缀 |
| header | Record<string, string> | 全局请求头 |
| timeout | number | 超时时间(ms),默认 15000 |
| getAccessToken | () => string \| undefined | 获取当前 Access Token |
| authHeaderKey | string | 鉴权请求头 Key,默认 Authorization |
| formatAuthValue | (token: string) => string | 格式化 Token 值,默认 Bearer <token> |
| skipAuth | (req) => boolean | 返回 true 时跳过 Token 注入 |
| refreshToken | 见下表 | 无感刷新配置 |
| isSuccess | (data) => boolean | 判断业务是否成功 |
| resolveData | (data) => any | 提取业务数据(成功时) |
| onBizError | (err) => void | 业务异常全局回调 |
| onHttpError | (statusCode, err) => void | HTTP 异常全局回调 |
| requestInterceptors | Middleware[] | 用户前置中间件 |
| responseInterceptors | Middleware[] | 用户后置中间件 |
| middlewares | Middleware[] | 额外注入的自定义中间件 |
refreshToken 配置
| 属性 | 类型 | 说明 |
|------|------|------|
| url | string | 刷新 Token 的接口地址(相对 baseURL) |
| method | string | 请求方法,默认 POST |
| data | () => Record<string, any> | 请求 Body 参数 |
| params | () => Record<string, any> | Query 参数 |
| header | Record<string, string> | 额外请求头 |
| resolveToken | (res) => string \| undefined | 从响应中解析新 Token |
| onRefreshed | (res) => void \| Promise<void> | 刷新成功回调(通常用于持久化新 Token) |
| onRefreshFailed | (err) => void | 刷新失败回调(通常用于跳转登录页) |
OnionRequest 实例方法
| 方法 | 说明 |
|------|------|
| request<T>(options) | 发起请求,返回 AbortablePromise<T> |
| get<T>(url, data?, options?) | GET 请求 |
| post<T>(url, data?, options?) | POST 请求 |
| put<T>(url, data?, options?) | PUT 请求 |
| delete<T>(url, data?, options?) | DELETE 请求 |
| upload<T>(url, filePath, data?, options?) | 文件上传(自动转换为 multipart/form-data) |
| use(middleware) | 手动注册中间件 |
所有方法返回的 AbortablePromise<T> 都支持 .abort() 主动取消请求:
const promise = http.get('/list')
promise.abort() // 立即取消自定义中间件
中间件签名与 Koa 一致:
import type { Middleware } from '@zero-bits/taro-request'
const timingMiddleware: Middleware = async (ctx, next) => {
const start = Date.now()
await next()
console.log(`耗时 ${Date.now() - start}ms`, ctx.req.url)
}
const http = createRequest({
baseURL: 'https://api.example.com',
requestInterceptors: [timingMiddleware],
})RequestContext
中间件中可读写的上下文对象:
| 属性 | 类型 | 说明 |
|------|------|------|
| req | Taro.request.Option | 请求参数,可在中间件中修改 |
| res | Taro.request.SuccessCallbackResult | 响应结果,请求完成后可读 |
| error | any | 错误信息 |
| state | Record<string, any> | 中间件间共享的数据沙盒 |
| state.task | Taro.RequestTask \| Taro.UploadTask | 底层请求任务实例 |
| state.abortSignal | boolean | 是否已发出取消信号 |
| request | (options) => Promise<any> | 重新发起请求(用于 retry 场景) |
taroAdapter(Axios 方案)
将 Axios 请求桥接到 Taro.request,支持:
- 普通 JSON 请求
arraybuffer二进制响应- 文件上传(
multipart/form-data,payload 中需含filePath和name) - Axios 的
cancelToken和signal取消机制
import axios from 'axios'
import { taroAdapter, defaultTransformRequest } from '@zero-bits/taro-request'
const instance = axios.create({
baseURL: 'https://api.example.com',
adapter: taroAdapter,
transformRequest: [defaultTransformRequest],
})defaultTransformRequest
替代 Axios 默认的序列化逻辑,兼容小程序环境:
- 普通对象 / 数组 →
JSON.stringify(自动设置Content-Type: application/json) application/x-www-form-urlencoded→ 原样透传(由小程序底层处理)FormData/ArrayBuffer/Blob/File→ 原样透传,不做序列化
setupTaroEnvironment
在小程序入口(app.ts)调用一次,补齐缺失的 AbortController 全局对象,防止部分上游库在小程序环境报错。
import { setupTaroEnvironment } from '@zero-bits/taro-request'
setupTaroEnvironment()错误处理
AbortError
主动调用 .abort() 时抛出,可用于区分业务错误与用户取消:
import { AbortError } from '@zero-bits/taro-request'
try {
await http.get('/list')
} catch (err) {
if (err instanceof AbortError) {
console.log('请求已取消')
} else {
console.error('请求失败', err)
}
}错误类型说明
| 错误 | 标志 | 说明 |
|------|------|------|
| HTTP 错误 | err.isHttpError === true | 状态码 < 200 或 ≥ 300 |
| 业务错误 | err.isBizError === true | isSuccess 返回 false |
| 取消错误 | err instanceof AbortError | 主动调用 .abort() |
无感刷新完整示例
import { createRequest, setupTaroEnvironment } from '@zero-bits/taro-request'
import Taro from '@tarojs/taro'
setupTaroEnvironment()
const http = createRequest({
baseURL: 'https://api.example.com',
timeout: 15000,
getAccessToken: () => Taro.getStorageSync('accessToken'),
isSuccess: (data) => data.code === 0,
resolveData: (data) => data.data,
onBizError: (err) => Taro.showToast({ title: err.msg || '请求失败', icon: 'none' }),
refreshToken: {
url: '/auth/refresh',
method: 'POST',
data: () => ({ refreshToken: Taro.getStorageSync('refreshToken') }),
resolveToken: (res) => res.data?.accessToken,
onRefreshed: (res) => {
Taro.setStorageSync('accessToken', res.data.accessToken)
Taro.setStorageSync('refreshToken', res.data.refreshToken)
},
onRefreshFailed: () => {
Taro.clearStorageSync()
Taro.reLaunch({ url: '/pages/login/index' })
},
},
})
export default http依赖版本
| 依赖 | 版本要求 |
|------|----------|
| react | >=18.0.0 |
| @tarojs/taro | >=4.2.1 |
| axios | >=1.19.0 |
License
MIT
