Axios API 风格的小程序 HTTP 请求库
特性
- 使用 TypeScript 编写,提供完整的类型定义
- 超过 95% 的单元测试覆盖率,提供稳定性保障
- 支持 创建多个实例
- 支持 校验状态码
- 支持 请求/响应拦截器
- 支持 转换请求和响应数据
- 支持 请求锁
- 支持 轮询
- 支持 上传/下载
- 支持 自定义适配器
- 适配 多端小程序(微信、支付宝、百度、抖音等)
安装
使用 pnpm
安装:
pnpm add @inottn/miniquest
使用 yarn
或 npm
安装:
yarn add @inottn/miniquest
npm i @inottn/miniquest
基本用例
默认导出了一个 MiniQuest
的实例:
import miniquest from '@inottn/miniquest';
import axios from '@inottn/miniquest';
import http from '@inottn/miniquest';
如果有需要,你也可以使用自定义配置新建一个实例:
import { create } from '@inottn/miniquest';
const miniquest = create({
baseURL: 'https://base.domain.com',
timeout: 1000,
headers: { 'X-Custom-Header': 'foobar' },
});
发送 GET 请求,miniquest
是 miniquest.request
的别名:
miniquest({ url: '/user?id=1' });
miniquest('/user?id=1');
miniquest.request('/user?id=1');
miniquest.get('/user?id=1');
miniquest.get('/user', {
data: { id: '1' },
});
发送 POST 请求:
miniquest({
url: '/user/create',
methods: 'post',
data: {
name: 'xxx',
},
});
miniquest('/user/create', {
methods: 'post',
data: {
name: 'xxx',
},
});
miniquest.post({
url: '/user/create',
data: {
name: 'xxx',
},
});
并发请求:
Promise.all([miniquest.get('/user?id=1'), miniquest.get('/user?id=2')]).then(
([user1, user2]) => {
},
);
实例方法
为了方便起见,为常用且支持的请求方法提供了别名。不同小程序平台支持的请求方法会有所不同,以实际情况为准:
-
miniquest.request(config)
-
miniquest.request(url[, config])
-
miniquest.delete(url[, config])
-
miniquest.download(url[, config])
-
miniquest.get(url[, config])
-
miniquest.head(url[, config])
-
miniquest.options(url[, config])
-
miniquest.post(url[, config])
-
miniquest.put(url[, config])
-
miniquest.upload(url[, config])
注意这和 axios 提供的实例方法略有不同,如果你更习惯 axios 提供的传参方式,你也可以基于此再封装一层:
export function post(url, data, config) {
return miniquest.post(url, {
...config,
data,
});
}
请求配置
这些是创建请求时可以用的通用配置选项。只有 url
是必需的。如果没有指定 method
,请求将默认使用 GET 方法。
实例配置和单次请求配置将会合并为最终请求时的配置,相同的选项,后者会覆盖前者。
由于运行小程序平台不同,平台特有的配置选项这里不再列出。你可以添加任何自定义字段,最终会透传给对应的小程序请求 API。
{
url: '/user',
method: 'get',
baseURL: 'https://some-domain.com/api/',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
transformRequest: null,
transformResponse: null,
skipLock: false,
validateStatus: function (status) {
return status >= 200 && status < 300;
},
adapter: function (config) {
},
}
响应结构
一个请求的响应包含以下通用信息,以及对应小程序平台返回的响应信息。
{
data: {},
status: 200,
headers: {},
config: {},
}
校验状态码
在微信小程序中,只要成功收到服务器返回,无论 HTTP 状态码是多少都会进入 success 回调。而在支付宝小程序中则还会校验 HTTP 状态码,例如 404、500、504 等状态码会触发 fail 回调。这个时候你可以使用 validateStatus
配置选项自定义哪些 HTTP 状态码是有效的,从而统一多端小程序行为。
miniquest.get('/user/12345', {
validateStatus: function (status) {
return status >= 200 && status < 300;
},
});
拦截器
使用拦截器在请求发起之前和收到响应数据之后对配置或响应做一些处理,并且可以在拦截器中执行异步任务。
miniquest.interceptors.request.use(
function (config) {
return config;
},
function (error) {
return Promise.reject(error);
},
);
miniquest.interceptors.response.use(
function (response) {
return response;
},
function (error) {
return Promise.reject(error);
},
);
多个拦截器
如果你添加了多个请求或响应拦截器,它们会按如下顺序执行:
- 先添加的请求拦截器后执行,后添加的请求拦截器先执行。
- 先添加的响应拦截器先执行,后添加的响应拦截器后执行。
数据转换器
transformRequest
允许在向服务器发送请求前,修改请求数据。它会在请求拦截器后执行:
{
transformRequest: [function (data, headers) {
return data;
}],
}
transformResponse
允许在接收到响应后,修改响应数据。它会在响应拦截器前执行:
{
transformResponse: [function (data) {
return data;
}],
}
拦截器和转换器的区别?
拦截器一般用于拦截和修改请求与响应的配置和信息,可以处理异步任务。而转换器一般用于对请求与响应的数据进行格式转换,只能进行同步操作。
请求锁
当请求锁定后,所有未发送的请求将依次等待,直到请求解锁时才能继续发送。请求锁提供了如下 API:
miniquest.lock()
将请求锁定miniquest.unlock()
将请求解锁,锁定期间待处理的请求将依次执行miniquest.interrupt()
将请求解锁,锁定期间待处理的请求将中断并抛出错误miniquest.isLocked()
返回请求是否锁定
在发送请求时,通常为了安全考虑,会将用于标识用户身份的 token 放在 headers 中。如果在此时尚未获取到 token,则一般会在获取到 token 后再发送请求。然而,若存在多个请求同时并发的情况,可能会导致多次发送获取 token 的请求。
在这个场景我们有两个解决办法,一是使用缓存,让同时发送获取 token 的请求返回的都是一个请求。二是使用请求锁,获取 token 时,将其余请求锁定,示例如下:
let token;
const getToken = () => {
};
miniquest.interceptors.request.use(function (config) {
if (!token) {
miniquest.lock();
return getToken()
.then((token) => {
config.headers.token = token;
miniquest.unlock();
return config;
})
.catch(() => {
miniquest.interrupt();
});
} else {
config.headers.token = token;
}
return config;
});
轮询
提供了 poll
和 poll.create
API 用来轮询。
调用 poll
后会立即开始轮询:
poll(() => miniquest.get('/user?id=1'), {
validate: (response) => response.success,
interval: 1000,
retries: 5,
});
也可以调用 poll.create
创建一个轮询函数:
const pollFn = poll.create(() => miniquest.get('/user?id=1'), {
validate: (response) => response.success,
interval: 1000,
retries: 5,
});
pollFn();