@seayoo-web/request v1.7.7
网络请求工具库
为什么
市面上流行的网络请求工具有以下一些限制或不足:
- 体积过大
- 流行的几个 request 工具均不默认支持重试配置
- 对响应结果不进行更多检查(无类型守卫,无结构分析),无法完美对接 ts 代码
- 功能过于冗余,而相当多特性实际开发中使用不到
特性
- 默认基于 fetch,请求和响应解析均默认基于 json 或 text
- 支持重试配置 maxRetry / retryResolve / retryInterval
- 支持响应结果处理策略 responseRule 和通用提示配置
- 支持类型守卫 typeGuard
- 支持响应内容命名风格转化(camelize / snakify)
- 支持多实例,多端(浏览器,nodejs,微信小程序)
- get 请求函数支持并发缓存(默认缓存 500ms)
- 函数永不抛错,返回固定解析结构 { ok, data, status, headers, code, message }
- 提供 jsonp / jsonx 函数,支持带上传进度的 upload 函数
示例
import { get } from "@seayoo-web/request"
interface IUser {
name: string,
id: string
}
function isUserList(data: unknown): data is IUser[] {
return Array.isArray(data)
&& data.every(item => !!item && typeof item === "object" && "name" in item && "id" in item)
}
export async function getUserList(): Promise<IUser[]> {
const { data } = await get("/api/user", isUserList)
return data || []
}
配置响应规则
// 浏览器环境
import { setGlobalConfig } from "@seayoo-web/request"
// nodejs 环境
import { setGlobalConfig } from "@seayoo-web/request/node"
// 或在某些编译配置或编辑器中无法识别上述路径,可以改用实际的 dist 路径导入
import { setGlobalConfig } from "@seayoo-web/request/dist/node"
// 以下是默认的全局配置(更多介绍请参考下方全局配置)
setGlobalConfig({
// api 基础路径,默认根目录,通常可以设置为 /api
baseURL: "",
// 响应解析规则
responseRule: {
// status 成功时解析策略:将 body 作为响应数据
ok: { resolve: "body" },
// status 失败时解析策略:将响应体解析为 json 并读取 message 字段作为错误消息
failed: { resolve: "json", messageField: "message" }
},
// 更多全局配置参考下方介绍
})
全局函数和自定义实例
// 以下为浏览器环境全局默认导出的工具函数
import { get, post, put, patch, del, head } from "@seayoo-web/request"
// 以下是 nodejs 环境全局默认导出的工具函数
import { get, post, put, patch, del, head } from "@seayoo-web/request/node"
// 以下是 微信小程序 环境全局默认导出的工具函数
// 注意,微信不支持 patch 方法
import { get, post, put, del, head } from "@seayoo-web/request/wx"
// 浏览器环境自定义创建实例
import { NetRequest } from "@seayoo-web/request"
const { get, post, put, patch, del, head, setConfig } = NetRequest()
// nodejs环境自定义创建实例
import { NetRequest } from "@seayoo-web/request/node"
const { get, post, put, patch, del, head, setConfig } = NetRequest()
// 微信小程序自定义创建实例
import { NetRequest } from "@seayoo-web/request/wx"
const { get, post, put, del, head, setConfig } = NetRequest()
全局配置参数
import { setGlobalConfig } from "@seayoo-web/request"
// 全局配置字段说明见下,所有字段均可选
// 全局配置仅仅影响全局导出函数,比如 get, post 等,对于自定义实例没有影响
setGlobalConfig({ baseURL: "/api" })
// 自定义实例全局配置是独立的
import { NetRequest } from "@seayoo-web/request"
// 可以创建时传递
const { get, post, setConfig } = NetRequest({ baseURL: "/api" })
// 也可以随时修改
setConfig({ timeout: 5000 })
baseURL
类型:string
说明:需要以 / 开头或者是一个完整的 url 地址;此设置会给所有请求的 url 增加 baseURL 作为前缀,但以下两种情况除外
- 请求时传入的 url 为完整 url,即以 http:// 或 https:// 开头
- 请求时传入的 url 以 / 开头
node 环境仅仅支持完整 url 地址
credentials
类型:"omit" | "same-origin" | "include"
说明:是否携带用户认证信息(cookie, basic http auth 等),默认 "same-orgin",当需要跨域发送 cookie 时可以设置为 include;当需要明确忽略 cookie(比如认证信息已经放入自定义 header 头)时,可以设置为 omit;
仅浏览器环境有效;如果运行环境不支持 fetch 则 omit 无效;
timeout
类型:number
说明:请求超时设置,单位 ms,默认 5000
cacheTTL
类型: number
说明:用于控制 get 请求的缓冲时效,单位 ms,默认 500,设置为 0 则禁用缓冲
message
类型:false | (( result: IResponseResult, method: string, url: string, defaultMessage: string ) => string | false | Error | {message: string})
说明:默认情况下,工具函数会输出 message 消息用于提示,当不需要提示时可以设置为 false;
如果需要自定义提示内容,可以传递一个函数,并返回 false 或 字符串; 如果需要指定提示的类型,可以选择返回:
- Error 对象,则消息格式会被强制以 error 方式提示
- { message: string } ,则消息格式会被强制以 success 方式提示
responseRule
类型:{ failed: FailedRule, ok: OKRule }
说明:用于指定如何解析响应体内容
FailedRule: { resolve, converter?, statusField?, messageField? }
http失败时 (status <200 || status >= 400) 解析策略
resolve: "json" | "body"
解析方式,设置为 json(默认),则可以进一步指定错误消息字段;设置为 body 则将整个 body 解析为错误信息;
converter: "camelize" | "snakify"
内容转化方式,默认不转化;设置 "camelize" 则所有字段转成驼峰格式,设置 "snakify" 则所有字段转成蛇形格式
statusField: string
解析错误消息的状态字段,仅在 resolve 为 json 时有效,有值的话会替换 response 的 code
messageField: string | string[]
错误消息解析字段,仅在 resolve 为 json 时有效,默认值 "message"
OKRule: { resolve, statusField?, statusOKValue?, dataField?, messageField?, ignoreMessage? }
http成功时 (200 <= status < 400) 解析策略
resolve: "json" | "body"
解析方式,若设置为 json,则可以进一步指定更多字段;若设置为 body(默认),则把整个响应体作为接口返回的数据使用,如果格式化失败,则返回响应的字符串;
converter: "camelize" | "snakify"
内容转化方式,默认不转化;设置 "camelize" 则所有字段转成驼峰格式,设置 "snakify" 则所有字段转成蛇形格式
statusField: string
指定表示自定义状态的字段名,默认是 "code"
statusOKValue: string
指定表示自定义状态成功时的 value,默认是 "0",如果响应值为数字,则会被转化为字符串进行处理
dataField: string | true
指定表示响应数据的字段,默认是 "data",如果设置为 true,则将整个 json body 作为数据内容返回
messageField: string | string[]
指定表示提示消息的字段,提示消息可以包括错误消息和成功消息,默认是 "message"
ignoreMessage: string | string[]
指定忽略的消息,比如 ok 等,可以设置多个忽略消息,区分大小写
maxRetry
类型:number
说明:当请求发生错误时重试的次数,默认0,即不重试,最大为 10
retryResolve
类型:"network" | "status" | number[] | ((result: IRequestBaseResponse, count: number) => boolean)
说明:重试判断方法,默认是 network
network 表示仅仅当网络错误时才重试;
status 表示网络错误或者http 状态码错误时重试;
当设置为 number[] 时,将检查 http 状态码,匹配则重试;
也可以设置为一个重试检查函数,返回 true 则进行重试;
retryInterval
类型:number | "2EB" | ((retryIndex: number) => number)
说明:两次重试的间隔策略,设置为数字(单位 ms)表示固定间隔,设置为函数则可以自定义间隔;
其中 retryIndex 从 1 开始,最大为 10;最小时间间隔为 100ms;
特殊值 2EB 表示以 2 为底的指数退避策略,即首次重试等待 1 秒,后续分别是 2秒,4秒,8秒 ...
requestTransformer
类型:null | ((data: { headers: Record<string, string>, params: Record<string, string>, method: string, url: string, body: IBaseRequestBody }) => MaybePromise<void | string>)
说明:可用于发送前修改请求相关数据,通常用于追加认证信息或签名信息
- 发送前用于修改 headers 或 params 的函数,headers 和 params 为引用数据,可以直接修改或追加内容;
- 函数如果返回一个非空字符串,则当作 url 地址使用,原有的 url 地址将被替换;
- 通常用于全局追加 header 自定义认证信息或签名信息;
errorHandler
类型:null | ((status: number, method: string, url: string, rawError?: Error | ProgressEvent) => void)
说明:全局错误处理函数,仅仅在 http status 错误时触发,通常用于检查 401 未登录状态进行跳转登录,其中 rawError 为请求工具的原始错误,可用以进行 Sentry 信息上报;
responseHandler
类型:null | ((result: IResponseResult, method: string, url: string) => void)
说明:请求完成后用于统一检查响应结果,非网络错误时触发,入参的数据均为副本,仅仅可读
messageHandler
类型:null | ((isError: boolean, message: string) => void)
说明:全局默认提示函数,isError 表示需要提示的消息是否为错误消息,message 为提示内容
logHandler
类型:null | ((data: IRequestLog) => void)
说明:全局日志打印函数,具体日志信息可参考源码类型声明;
网络请求参数
类型:IRequestOptions
说明:网络请求参数,对于每个最终工具函数都提供有可选的请求参数,且每个字段均可选
method
类型:"GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD"
params
类型:Record<string, unknown>
headers
类型: Record<string, string>
body
类型:Blob | ArrayBuffer | FormData | URLSearchParams | string | Record<string, unknown>
说明:请求要发送的数据,当 method 为 GET / HEAD / DELETE 时,body 无效
credentials
同全局配置,仅本次请求有效,可选 "omit" | "same-origin" | "include",默认 same-origin,如果跨域需要携带 cookie,可以设置为 include
timeout
同全局配置,仅本次请求有效,默认 5000
abort
类型:AbortSignal,用于主动中断请求;
- 当请求结束时无效;
- 仅浏览器环境有效,
- 不支持 AbortSignal 的浏览器可以引入 https://www.npmjs.com/package/abortcontroller-polyfill 或 https://polyfill.io/v3/polyfill.min.js?features=AbortController
message
同全局配置,仅本次请求有效,可以设定 false 关闭消息提示,也可以传递函数自定义消息内容;
responseRule
同全局配置,设定的解析规则仅本次请求有效
maxRetry
同全局配置,仅本次请求有效,设置最大重试次数,默认 0,即不重试
retryResolve
同全局配置,仅本次请求有效,设置重试判断策略,默认 network
retryInterval
同全局配置,仅本次请求有效,默认 1000ms
响应内容
所有请求均返回同样的结构(包括网络错误或状态码错误时)其结构字段如下
ok
类型:boolean
说明:表示当前请求是否成功,包括检测 http status 以及 responseRule
status
类型:number
说明:表示响应的 http 状态码,网络错误或其他异常情况返回负数(具体数值无含义)
code
类型:string
说明:表示当前请求的状态信息,其可能值有
- http statusText 当请求完成且无自定义错误码时
- responseRule 中自定义的错误码字段所传递的值
- "NetworkError","Failed","Aborted" 或 "Unknown" 网络错误或取消等异常情况
message
类型:string
说明:响应结果的提示消息,不受 IRequestOptions.message 影响
headers
类型:Record<string, string | undefined>
说明:响应的头信息,全部字段名均被转成小写
data
类型:unknown | T | null
说明:响应的数据内容,其可能值为:
- unknown:如果不提供类型守卫,则返回 unknown
- null: 网络请求错误、类型守卫检查失败、服务器没有返回正确格式的 json 数据,则返回 null
- T: 如果成功返回并通过类型守卫检查,则返回类型守卫对应的类型
类型守卫
类型守卫是一个返回 boolean 的特殊函数,用于检查某个 unknown 的数据是否为指定类型的数据,对于 ts 来说,这是确保远程数据类型安全的必要措施。其定义如下
type TypeGuardFn<T> = (value: unknown) => value is T;
type TypeGuard<T> = {
guard: TypeGuardFn<T>;
message: string; /* 当检查失败时的错误提示信息 */
};
type TypeGuardParam<T> = TypeGuard<T> | TypeGuardFn<T>
Request函数
get\<T>(url: string, typeGard?: TypeGuardParam\<T> | null, options?: IRequestOptions)
- url: 请求的资源路径,如果需要跳过全局 baseURL 设置,可以传递完整 url 地址,或者以 / 开头,node 环境仅仅支持完整 url 地址
- typeGuard: 可选类型守卫,如果设置为 null,则返回的内容为 unkonwn,否则就是经过类型守卫检查后的类型化数据,推荐传递类型守卫
- options: 可选请求配置参数
注意,get 请求默认设置 500ms 缓存,即对同一个请求(url+params)在 500ms 内不再重复发起请求,而是直接返回上一次的结果
post(url: string, data: RequstBody, typeGard?: TypeGuardParam\<T> | null, options?: IRequestOptions)
- url: 请求的资源路径,如果需要跳过全局 baseURL 设置,可以传递完整 url 地址,或者以 / 开头,node 环境仅仅支持完整 url 地址
- data: 请求发送的数据,等同于 IRequestOptions.body
- typeGuard: 可选类型守卫,如果设置为 null,则返回的内容为 unkonwn,否则就是经过类型守卫检查后的类型化数据,推荐传递类型守卫,除非对响应内容不关心(比如 httpStatus 为 204 或 202)
- options: 可选请求配置参数
Other
head(url, options?) head 方法不支持响应body,也就无需 typeGuard
del(url, typeGard?, options?)
put(url, data, typeGard?, options?)
patch(url, data, typeGard?, options?)
特殊函数
工具还提供了三个特殊函数以应对不同的场景需求,且限于浏览器环境使用:
upload(url: string, files: Record<string, Blob>, options?)
因为 fetch 不支持上传进度信息,故使用 xhr 函数包装,支持带有上传进度信息的 upload 函数,以满足特定场景下的大文件上传需求;
其可选 options 不支持重试,其 body 类型限制为 Record<string, unknown>,并且随着 formData 发送;同时新增进度信息配置:
onUploadProgress?: (progress: { total: number; loaded: number }) => void
jsonp\<T>(url: string, typeGuard: TypeGuardFn\<T>, params?: Record\<string, string|number|boolean>)
以 script 方式加载远程资源,并通过 callback 回调接收远程数据,必须提供类型守卫;
jsonx\<T>(url: string, typeGuard: TypeGuardFn\<T>, params?: Record\<string, string|number|boolean>)
以 script 方式加载远程资源,并通过 var(全局变量)接收远程数据,必须提供类型守卫;
通常情况下,并不推荐使用 jsonp 和 jsonx 来请求数据,跨域请求请优先使用后端 CORS 方案;
更多示例
使用 form
发送数据(所有数据均可以序列化为字符串)
const encodeFormData: string[]
for(const key in data) {
encodeFormData.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`);
}
// 需要手工指定Content-Type
const { ok, data } = await post(url, encodeFormData.join("&"), typeGuard, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
});
使用 formData
发送数据(可以含有二进制文件),如果是想要带进度的上传,可以使用 upload
方法
const formData = new FormData();
for(const key in data) {
formData.append(key, data[key]);
}
// 传递 FormData 实例时,无须手工指定 Content-Type
const { ok, data } = await post(url, formData, typeGuard);
修改默认的异常提示
setGlobalConfig({
message: function({ code, status, message }, method, url, defaultMsg){
if(code === "NetworkError" || code === "Failed" || code === "Aborted") {
return "⚠️ 网络错误,请检查网络";
}
if(status === 404) {
return `${method} ${url} 接口未定义`;
}
if(status >= 500 && status <= 599) {
return "🚨 服务器错误,请稍候再试";
}
if(code === "Unknown") {
return `⛔ ${url} 出现未知错误,请稍候再试`;
}
return message;
}
})
8 months ago
8 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago