@wsvaio/api
v2.0.0-12
Published
一个使用 TypeScript 编写,基于 fetch 的网络请求库,主要用于处理 HTTP 请求
Downloads
81
Readme
@wsvaio/api
一个使用 TypeScript 编写,通用的网络请求库,主要用于处理 HTTP 请求
特性
- ✨ 通用的现代网络请求库,可以兼容多种环境
- 🎉 使用 TypeScript 编写,完善的类型支持
- 🎨 支持请求中间件,方便扩展功能
- 🎇 提供创建 API、设置全局上下文、执行请求等功能
- 🎏 支持合并上下文和配置,方便定制请求行为
- 🤖 内置实用中间件,如 URL 拼接、返回结果检查等
- 👾 支持日志输出,方便调试和查看请求情况
- 🐲 提供柯里化配置,优雅的封装接口
- 🐳 易于使用,帮助快速处理各种网络请求
API
快速使用
安装
npm install @wsvaio/api
使用方法
首先,需要引入请求库:
import { createNativeFetch } from "@wsvaio/api";
接下来,可以创建一个 API 实例:
// 创建api实例并带有两个自定义属性
export const { post, get, put, patch, del, request, use } = createByNativeFetch<{
success?: string; // 请求成功时的消息
noticeable?: boolean; // 是否需要通知
}>({
base: "https://api.example.com",
log: true,
noticeable: true,
headers: {
// headers...
},
// 其他配置选项...
});
配置请求中间件:
// before 请求发出前
use("before")(async () => Progress.start());
// 设置请求token
use("before")(async ctx => {
const auth = useAuthStore();
ctx.headers.Authorization = `Bearer ${auth.accessToken}`;
});
// after 请求发出后
// 抛出错误
use("after")(async ctx => {
if (ctx.data?.code != 200) throw new Error(ctx.message);
});
// data扁平化
use("after")(async ctx => (ctx.data = ctx.data.data));
// error 错误处理
// 单独处理401
use("error")(async ctx => {
if (ctx.data?.code != 401) return;
// handle...
});
// final 收尾
use("final")(async ctx => Progress.done(!ctx.error));
// 通过扩展自定义属性实现通知
use("final")(async ctx =>
ctx.error && ctx.noticeable
? ctx.message && ElNotification.error(ctx.message)
: ctx.success && ElNotification.success(ctx.success)
);
您可以预先定义请求接口:
// 简单定义
export const getUser = get("/user/:id");
export const addUser = post("/user");
// 或者传入一个对象
export const editUser = put({
url: "/user/:id",
param: { id: 1 }, // param参数
body: { username: "oiavsw" },
config: true, // 必须设置config=true,否则将会执行请求
});
现在,可以使用 api 实例发起请求:
// 直接发送请求
get({ url: "/users" }).then(data => {
console.log(data);
});
// POST 请求
post({
url: "/users",
body: {
name: "张三",
age: 30,
},
}).then(data => {
console.log(data);
});
// 发送预先定义的请求
getUser({
// 请求后显示通知
noticeable: true,
// 请求成功时的通知
success: "获取成功"
// param参数
param: { id: 1 },
}).then(data => {
// 响应
console.log(data);
});
addUser({
// body 参数
body: {
username: 'wsvaio'
}
}).then(data => {
// 响应
console.log(data);
});
editUser();
通用性
通过提供不同的requester,可兼容不同的平台
内置:
- nativeFetchRequester //原生fetch
- uniappRequester // uniapp环境下的request
- nuxtFetchRequester // nuxt环境下的$fetch
例如要兼容uniapp,则可以使用uniappRequester
import { create, uniappRequester } from "@wsvaio/api";
export const { get } = create(uniappRequester)({
base: "http://localhost",
log: true,
});
get({
q: { q1: 1 },
});
createByNtiveFetch({...}) 相当于 create(nativeFetchRequester)({...})
自定义requester
通过 defineRequester 可以自定义请求器,例如一个简易的fetch请求器:
import { defineRequester } from "@wsvaio/api";
export const myNativeFetchRequester = defineRequester(
async (
ctx: CoreContext<{
// ……自定义类型
}>
) => {
const response = await fetch(ctx.url);
return {
// 必须返回这三个属性
data: await response.json(),
message: response.statusText,
status: response.status,
// 可以额外返回一些自定义属性
test: "my requester",
};
}
);
通用性的核心就是通过定义不同环境下的requester来实现的
中间件
本请求库支持使用中间件来扩展功能。以下是一些示例:
请求前中间件
api.use("before")(async ctx => {
console.log("请求前");
});
请求后中间件
api.use("after")(async ctx => {
console.log("请求后");
});
错误处理中间件
api.use("error")(async ctx => {
console.log("错误处理");
});
最终处理中间件
api.use("final")(async ctx => {
console.log("最终处理");
});
柯里化配置
get、post 等http方法是柯里化的,可以无限递归,专门用于封装接口。必须设置 { config: true } 才会递归下去,否则将会执行请求
// 创建配置
import { createByNativeFetch } from "@wsvaio/api";
export const { get, request } = createByNativeFetch();
// 柯里化配置
const getTest1 = get({ url: "/test", config: true });
const getTest2 = get({ query: { q1: 1 }, config: true })({ param: { p1: 1 }, config: true })({
body: { b1: 1 },
config: true,
});
const getTest3 = get("/test"); // 相当于 { path: '/test', config: true }
// 发送请求
getTest1({ query: { p1: 1 } }).then(data => console.log(data));
getTest2({ query: { p2: 2 } }).then(data => console.log(data));
getTest3().then(data => console.log(data));
// 获取context
const context = getTest3();
console.log(context)
// 获取data
const data = await context;
Typescirpt
类型支持!不同的requester也会有不同的类型
柯里化的泛型支持
// 泛型支持,可无限递归配置,对当前无影响,对递归的下一级有影响,后续则都为可选
// data 为特殊保留字段,不会作用,但会影响返回值的类型
const getUser = get<{
body: {}; // 配置body类型
query: {}; // 配置query类型
param: {}; // 配置param类型
data: {}; // 配置data返回结果
}>("/user");
// 支持递归
getUser<{
body: {};
}>({});
扩展、继承 API 实例
将要继承的ctx作为参数传入,即可扩展一个新的 API 实例:
// 创建配置
import { createNativeFetchAPI, mergeContext } from "@wsvaio/api";
export const { ctx } = createNativeFetchAPI({
baseURL: "/api",
});
// 继承父级的配置
const { get } = createNativeFetchAPI({
...ctx,
// 甚至多个父级
...mergeContext(ctx1, ctx2, ctx3...),
other: {},
});
// 发送请求
get({ url: "/test" });
日志打印
// 创建配置
import { createByNativeFetch } from "@wsvaio/api";
export const { get } = createByNativeFetch({
log: true, // 日志打印
});
额外扩展示例
通过自定义类型+中间件+柯里化配置等,可以灵活的控制请求过程,甚至添加一些额外的实用功能
只有想不到,没有做不到
整体思路
通过自定义类型,要求调用者可以传入一些自定义属性
在中间件下对实现相关功能,与自定义属性实现联动
只需要在before中间件将q、b、p与query、body、param赋值便可
import { createByNativeFetch } from "@wsvaio/api";
export const { post, get, put, patch, del, request, use, ctx } = createByNativeFetch<{
b?: Record<any, any>;
q?: Record<any, any>;
p?: Record<any, any>;
}>({
// ...
});
use("before")(async ctx => {
ctx.q && Object.assign(ctx.query, ctx.q);
ctx.b && !ctx.body && (ctx.body = ctx.b);
ctx.p && Object.assign(ctx.param, ctx.p);
});
// 封装接口,传入泛型,只需要定义p的类型便可
export const getUserInfo = get<{ p: { id: number } }>("/user/:id");
// 使用时会有类型校验,必须传入p.id,否则报错
getUserInfo({ p: { id: 1 } });
全局的错误消息通知或成功消息通知
自定义错误消息,成功消息,可以自定义是否通知
export const { get, use } = createByNativeFetch<{
sucMsg?: boolean | string; // 操作成功时的消息,传入布尔值代表请求成功是否通知ctx.message,传入字符串代表请求成功后通知该内容
errMsg?: boolean | string; // 操作失败时的消息,传入布尔值代表请求报错是否通知ctx.message,传入字符串代表请求报错后通知该内容
}>({
// ...
errMsg: true, // 默认进行错误通知
});
// 统一message
use("final")(async ctx => {
// 可以为业务msg,或是http状态,等等由实际业务决定
ctx.message = ctx.data.msg || ctx.response.statusText || ctx.message;
});
// 成功提示,优先级为ctx.sucMsg > ctx.message
use("final")(async ctx => {
if (!ctx.sucMsg || ctx.error)
return;
const message = ctx.sucMsg === true || typeof ctx.sucMsg !== "string" ? ctx.message : ctx.sucMsg;
// 这里用element组件的api进行通知
ElMessage.success(message);
});
// 错误提示,优先级为ctx.errMsg > ctx.message
use("final")(async ctx => {
if (!ctx.errMsg || !ctx.error)
return;
const message = ctx.errMsg === true || typeof ctx.errMsg !== "string" ? ctx.message : ctx.errMsg;
// 这里用element组件的api进行通知
ElMessage.error(message);
});
// 定义接口
const getUserInfo = get<{ param: { id: number } }>("/user/:id");
// 使用
// 请求成功后会通知“获取成功”,请求失败后会通知“获取失败”
getUserInfo({
param: { id: 1 },
sucMsg: "获取成功",
errMsg: "获取失败",
});
// 请求成功或失败都不会有通知
getUserInfo({
param: { id: 1 },
sucMsg: false,
errMsg: false,
});
2.0更新了什么?
- 通过不同的requester,支持不同的运行环境,例如原生web使用nativeFetachRequester、uniapp使用uniappRequester
- 自定义requester、通过defineRequester可以自定义请求器,您甚至可以将ajax(xhr)作为请求器使用!并且有完善的类型支持!
- 代码、类型近乎完全的重构,代码结构更加合理,类型支持更加完善
- log样式更新,新增请求耗时的展示
- baseURL 和 url 更改为 base 和 path,同时添加了fullPath,url更改
- base:原baseURL,请求基本路径或链接
- path:原url,请求路径
- fullPath:query与param拼接后的路径
- url:请求的完整链接地址(base + fullPath)
- 对相关api命名更改
- createAPI -> createByNativeFetch
- run -> exec
- ……
- 无限柯里化配置回归!,带有完善的类型支持
- 移除b、q、p配置(body、query、param)的别名,但可以自己通过before中间件实现
- ……
迁移至2.0
2.0 带来了些许破坏式更新,但他们大多是一些命名的更新,迁移难度不高
createAPI 更改为 createByNativeFetch,若要从@wsvaio/uniapp迁移,只需要
const createAPI = create(unqippRequester)
即可b、q、p支持移除,可添加前置中间件实现 为query、body、param添加别名支持q、b、p
baseURL 和 url 更改为 base 和 path,可通过全局替换迁移,或添加前置中间件
use("before")(async ctx => { ctx.base = ctx.baseURL; ctx.path = ctx.url; // 若有报错可忽略 delete ctx.baseURL; delete ctx.url; });
无限柯里化的回归,请在非调用配置中添加
{ config: true }
柯里化泛型,D -> data,请用data声明类型
get<{ D: string }>(/test) -> get<{ data: string }>(/test)
……
后续
- 考虑更改架构(monorepo),主要是将不同平台的requester抽离出去
- 考虑更改包名称(实在想不出好名字),欢迎提issue
- 有生之年更新完善的文档(vitepress)
- 持续的更新优化代码
源码
源码可以在 GitHub 仓库 中找到。
贡献
如果您发现@wsvaio/api 中有任何问题或缺少某些功能,请随时提交问题或请求。
欢迎您的贡献,包括提交错误修复、添加新功能或改进文档。