1.7.7 • Published 8 months ago

@seayoo-web/request v1.7.7

Weekly downloads
-
License
ISC
Repository
-
Last release
8 months ago

网络请求工具库

为什么

市面上流行的网络请求工具有以下一些限制或不足:

  1. 体积过大
  2. 流行的几个 request 工具均不默认支持重试配置
  3. 对响应结果不进行更多检查(无类型守卫,无结构分析),无法完美对接 ts 代码
  4. 功能过于冗余,而相当多特性实际开发中使用不到

特性

  1. 默认基于 fetch,请求和响应解析均默认基于 json 或 text
  2. 支持重试配置 maxRetry / retryResolve / retryInterval
  3. 支持响应结果处理策略 responseRule 和通用提示配置
  4. 支持类型守卫 typeGuard
  5. 支持响应内容命名风格转化(camelize / snakify)
  6. 支持多实例,多端(浏览器,nodejs,微信小程序)
  7. get 请求函数支持并发缓存(默认缓存 500ms)
  8. 函数永不抛错,返回固定解析结构 { ok, data, status, headers, code, message }
  9. 提供 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 作为前缀,但以下两种情况除外

  1. 请求时传入的 url 为完整 url,即以 http:// 或 https:// 开头
  2. 请求时传入的 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,用于主动中断请求;

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;
    }
})
1.7.7

8 months ago

1.7.6

8 months ago

1.7.5

12 months ago

1.7.4

1 year ago

1.7.3

1 year ago

1.7.2

1 year ago

1.7.1

1 year ago

1.7.0

1 year ago

1.6.7

1 year ago

1.6.6

1 year ago

1.6.5

1 year ago

1.6.4

1 year ago

1.6.3

1 year ago

1.6.2

1 year ago

1.6.1

1 year ago

1.6.0

1 year ago

1.5.6

1 year ago

1.5.5

1 year ago

1.5.4

1 year ago

1.5.3

1 year ago

1.5.2

1 year ago

1.5.1

1 year ago

1.5.0

1 year ago

1.4.1

1 year ago

1.4.0

1 year ago

1.3.3

1 year ago

1.3.2

1 year ago

1.3.1

2 years ago

1.3.0

2 years ago

1.2.0

2 years ago

1.2.2

2 years ago

1.2.1

2 years ago

1.1.9

2 years ago

1.1.8

2 years ago

1.1.7

2 years ago

1.1.6

2 years ago

1.1.5

2 years ago

1.1.4

2 years ago

1.1.3

2 years ago

1.1.2

2 years ago

1.1.1

2 years ago

1.1.0

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago

0.9.2

2 years ago

0.9.1

2 years ago

0.9.0

2 years ago

0.8.1

2 years ago

0.8.0

2 years ago

0.7.0

2 years ago

0.6.1

2 years ago

0.6.0

2 years ago

0.4.3

2 years ago

0.4.2

2 years ago

0.4.1

2 years ago

0.4.0

2 years ago

0.3.4

2 years ago

0.3.3

2 years ago

0.3.2

2 years ago

0.3.1

2 years ago

0.3.0

2 years ago

0.2.3

2 years ago

0.2.2

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago