@sttot/axios-api
v0.2.5
Published
为什么使用 api:
Downloads
3
Readme
Api - 基于 Axios 的标准化请求定义
为什么使用 api:
- 逻辑分离:定义 API 和使用 API 可以分开,使用者无需自行调用 axios 并考虑相关的请求、错误拦截等逻辑;
- 规范化:简洁地定义和使用 API,并提供一整套 TypeScript 类型推断;
- 可拓展性:可自行设置 axios、请求预处理等操作;
安装
需要配合 axios 一起使用:
pnpm install @sttot/axios-api axios
如果你使用 React,建议使用 @sttot/axios-api-hooks
,提供了更多有用的工具。
import axios from 'axios';
import { apiBase } from '@sttot/axios-api';
// 创建一个 api 工厂,使用我们引入的 axios
const api = apiBase(axios);
在 Cocos 中使用
请参考 暂时想了一个解决 npm 包引入问题的临时方案 中对 Axios 的引入方法。
使用
首先我们看看如何只使用 axios 进行请求:
import axios from 'axios';
// 定义数据类型
interface IInfo {
id: number;
data: string;
}
const getInfo = async (id: number): IInfo | undefined => {
try {
// 正式请求之前做的事情
console.log(`Trying to get info: ${id}`);
if (id < 0) {
throw new Error('id should bigger or equal than 0');
}
// 正式请求
const response = await axios({
method: 'GET',
baseUrl: 'https://api.sttot.com/info/',
url: `${id}`,
});
// 处理响应之前做的事情
console.log(`Got info: ${id}`);
// 处理响应 + 校验
const { data } = response;
if (data === undefined || data.id !== id || typeof data.data !== 'string') {
throw new Error('Invalid info data');
}
// 得到数据
return data as IInfo;
} catch (error) {
// 处理错误 + 数据回滚
console.error(`Fail to get info ${id}`, error);
return undefined;
}
};
如上请求包含了对请求参数的校验和解析,并在出错时提供回滚数据,我们可以将整个请求分为如下几部分:
- 在正式请求之前做一些事情,比如
console.log
打印一些信息、校验参数等; - 使用传入的参数 (
id
) 生成 RequestConfig,即{ method: ... }
这一部分,用 RequestConfig 进行请求; - 得到响应后,在处理响应之前做一些事情,比如打印日志;
- 对响应数据进行处理和校验;
- 处理以上过程中产生的错误,如果需要,则提供回滚数据;
生产环境中,如上的每个步骤都应当有,但是无论是使用 Api 的人还是定义 Api 的人都不愿意写如此长的带吗,于是会「偷工减料」,或者自己造轮子。因此使用一个相对简洁和规范的 Api 定义工具是必要的。
使用该框架可以将如上代码改写为:
// 定义一个 GET 请求
interface IInfo {
id: number;
data: string;
}
// 模板的第一个参数是 props 的类型,第二个参数是 result 的类型
// 还可以指定 Error 的类型等,更多请查看代码注释
const getInfoApi = api<number, IInfo | undefined>(
// 生成 RequestConfig,进行请求
id => ({
method: 'GET',
baseUrl: 'https://api.sttot.com/info/',
url: `${id}`,
}),
// 处理响应 + 校验
({ data }) => {
if (data === undefined || data.id !== id || typeof data.data !== 'string') {
throw new Error('Invalid info data');
}
return data;
},
// 处理错误 + 数据回滚
({ error }) => {
console.error(`Fail to get info ${id}`, error);
return undefined;
},
)
// 正式请求之前做的事情
.setBeforeRequest((_, { id }) => {
console.log(`Trying to get info: ${id}`);
if (id < 0) {
throw new Error('id should bigger or equal than 0');
}
})
// 处理响应之前做的事情
.setAfterResponse((_, { id }) => console.log(`Got info: ${id}`));
虽然没有让代码变得更短,但实际上定义 Api 时需要操心的事情变少了,而且除了 api 的第一个参数(用于生成 RequestConfig)和第二个参数(用于处理结果),其余的部分都是可选的,实际上需要做什么处理可以由开发者自行决定。
在定义之后,可以很简单的使用之:
const info = await getInfoApi(1);
如果需要终止请求,可以使用 AbortController
作为第二个参数:
const controller = new AbortController();
// 1s 后取消请求
setTimeout(() => controller.abort(), 1000);
const info = await getInfoApi(1, controller);
这里重新对 Api 的设计思路进行解释:
- 一个 Api 本身是一个黑盒,接受参数(props),进行请求(call),并返回结果(result),也可能会抛出异常,整个过程是异步的;
- 因此,一个最简单的 Api 应该包含三个流程:处理调用的参数(CallHandler) => 进行 Axios 请求(AxiosCall) => 对请求的响应进行处理以得到数据(ResultHandler)。
- 某些情况下,需要在进行 Axios 请求前进行额外的工作(Action Before Request),例如申请 AccessToken;
- 在某些情况下,需要在 Axios 请求结束、得到响应后进行额外的工作(Action After Response),例如更新进度条;
- 如上的五个过程都有可能抛出异常,因此可以对其进行处理并做一些相关的工作(ErrorHandler),进一步的,可能会重新抛出原有的/新的异常,或者返回一个默认数据;
因此整个过程可以设定五个处理函数:
- CallHandler: 必须有,将请求参数转化为 RequestConfig,可以抛出异常,异步或者同步;
- ResultHandler: 必须有,将响应转化为最后返回的数据,可以抛出异常,异步或者同步;
- ErrorHandler: 可选,如果没有,Api 会抛出所有的异常;如果有,将会由该函数处理,可以自行决定是继续抛出异常,还是返回一个默认数据,异步或者同步;
- Action(Before Request): 可选,在 CallHandler 执行之后、进行 Axios 请求之前做一些事情,可以抛出异常,异步或者同步;
- Action(After Response): 可选,在 Axios 响应之后、ResultHandler 之前之前做一些事情,可以抛出异常,异步或者同步;
一个 Api 的定义采用参数初始化 CallHandler、ResultHandler和ErrorHandler + 链式调用进行各种设置的方式,所有链式调用函数列如下:
- setAxiosInstance 设置 Axios 实例;
- setDefaultAxiosConfig 设置默认的请求,会与 CallHandler 的结果合并;
- setUrl 设置默认的 URL;
- setMethod 设置默认的请求方法;
- handleCall 重新设定 CallHandler;
- handleResult 重新设定 ResultHandler;
- handleError 重新设定 ErrorHandler;
- setBeforeRequest 设定 Action(Before Request);
- setAfterResponse 设定 Action(After Response);
具体用法参考 API 注释。
其他问题
如何在 ResultHandler 中拿到 RequestConfig
response.config 就是。
更多使用方法可以参考 Axios 官方文档。
如何在请求失败时重试请求
在 ErrorHandler 接受参数中,有 retry 函数,调用即会重新进行请求,重试的条件请自行判断。