3.2.0 • Published 7 months ago

@seayoo-web/request v3.2.0

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

网络请求工具库

为什么

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

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

特性

  1. 默认基于 fetch,请求和响应解析均默认基于 json 或 text
  2. 支持重试配置 maxRetry / retryResolve / retryInterval
  3. 支持响应结果处理策略 responseRule 和通用提示配置
  4. 支持类型守卫 typeGuard
  5. 支持多实例,多端(浏览器,nodejs,微信小程序)
  6. get 请求函数支持并发缓存(默认缓存 500ms)
  7. 函数永不抛错,返回固定解析结构 { ok, data, status, headers, code, message }
  8. 提供格式化良好的 sentry 信息用于错误上报
  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 基础路径,默认根目录
  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: 10000 });

baseURL

类型:string

说明:需要以 / 开头或者是一个完整的 url 地址;此设置会给所有请求的 url 增加 baseURL 作为前缀,但以下情况除外

  1. 请求时传入的 url 为完整 url,即以 http:// 或 https:// 开头

node 环境仅仅支持完整 url 地址作为 baseURL

credentials

类型:"omit" | "same-origin" | "include"

说明:是否携带用户认证信息(cookie, basic http auth 等),默认 "same-orgin",当需要跨域发送 cookie 时可以设置为 include;当需要明确忽略 cookie(比如认证信息已经放入自定义 header 头)时,可以设置为 omit;

仅浏览器环境有效;如果运行环境不支持 fetch 则 omit 无效;

timeout

类型:number

说明:请求超时设置,单位 ms,默认 10000,即 10 秒

cacheTTL

类型:number

说明:用于控制 get 请求的缓冲时效,单位 ms,默认 500,设置为 0 则禁用缓冲

defaultTypeGuardMessage

类型:string

说明:默认的类型守卫错误提示,仅支持全局配置,默认 “响应数据未能正确识别“,单个 api 可通过传入完整的类型守卫({ guard, message })来定制提示消息。

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: (body: unknown) => unknown

    内容转化方式,默认不转化;可以提供函数进行转化,此时尚未进行类型守卫检查。body内容被 parseJSON 后作为参数传入转化函数。

    statusField: string

    解析错误消息的状态字段,仅在 resolve 为 json 时有效,有值的话会替换 response 的 code

    messageField: string | string[]

    错误消息解析字段,仅在 resolve 为 json 时有效,默认值 "message"

  • OKRule: { resolve, converter?, statusField?, statusOKValue?, dataField?, messageField?, ignoreMessage? }

    http成功时 (200 <= status < 400) 解析策略

    resolve: "json" | "body"

    解析方式,若设置为 json,则可以进一步指定更多字段;若设置为 body(默认),则把整个响应体作为接口返回的数据使用,如果格式化失败,则返回响应的字符串;

    converter: (body: unknown) => unknown

    内容转化方式,默认不转化;可以提供函数进行转化,此时尚未进行类型守卫检查。body内容被 parseJSON 后作为参数传入转化函数。

    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 表示仅仅当网络错误时才重试,如果请求被取消(aborted)则不重试;

status 表示网络错误或者http 状态码错误时(<200 || >=400)重试;

当设置为 number[] 时,将检查 http 状态码,匹配则重试;

也可以设置为一个重试检查函数,返回 true 则进行重试;

如果需要收集错误的重试信息,可以通过设置 retryResolve 为一个函数来检查或上报请求结果

retryInterval

类型:number | "2EB" | ((retryIndex: number, { url, method, status }) => number)

说明:两次重试的间隔策略,设置为数字(单位 ms)表示固定间隔,设置为函数则可以自定义间隔;默认是 100ms;

其中 retryIndex 从 1 开始,最大为 10;最小时间间隔为 100ms;

特殊值 2EB 表示以 2 为底的指数退避策略,即首次重试等待 0.1 秒,后续分别是 0.2秒,0.4秒,0.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 | ((data: { status: number, code: string, method: string, url: string, headers: Record<string, string>, rawError?: Error | ProgressEvent, responseBody: string, sentryError: Error, sentryTags: Record<string, string | number>, sentryExtra: Record<string, unknown> }) => void)

说明:全局错误处理函数,仅仅在 http status 错误时触发,通常用于检查 401 未登录状态进行跳转登录,其中 sentryError / sentryTags / sentryExtra 可用以进行 Sentry 信息上报(示例代码见下);

responseHandler

类型:null | ((result: IResponseResult, method: string, url: string) => void)

说明:请求完成后用于统一检查响应结果,非网络错误时触发,入参的数据均为副本,仅仅可读

messageHandler

类型:null | ((isError: boolean, message: string, code: string, status:number) => void)

说明:全局默认提示函数,isError 表示需要提示的消息是否为错误消息,message 为提示内容,code 为状态描述码或内置错误码(可导出 RequestInternalError 进行对比),status 为 http 状态码

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",如果跨域需要携带 cookie,可以设置为 include。默认跟随全局设置

timeout

同全局配置,仅本次请求有效,默认跟随全局设置

abort

类型:AbortSignal,用于主动中断请求;

message

同全局配置,仅本次请求有效,默认跟随全局设置

responseRule

同全局配置,设定的解析规则仅本次请求有效,默认跟随全局设置

maxRetry

同全局配置,仅本次请求有效,设置最大重试次数,默认跟随全局设置

retryResolve

同全局配置,仅本次请求有效,设置重试判断策略,默认跟随全局设置

retryInterval

同全局配置,仅本次请求有效,默认跟随全局设置

响应内容

所有请求均返回同样的结构(包括网络错误或状态码错误时)其结构字段如下

ok

类型:boolean

说明:表示当前请求是否成功,包括检测 http status, responseRule 以及类型守卫

status

类型:number

说明:表示响应的 http 状态码,请求被取消返回 0,网络错误或其他异常情况返回负数(具体数值无含义)

code

类型:string

说明:表示当前请求的状态信息,其可能值有

  • http statusText 当请求完成且无自定义错误码时,可能为空字符串
  • responseRule 中自定义的错误码字段所传递的值
  • "Aborted" 或 "Unknown" 网络错误或取消等异常情况,详细参见 RequestInternalError

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函数

request\<T>(url: string, options?: IRequestOptions)

底层实现的 request 工具函数,未绑定类型守卫处理,返回的数据类型固定为 unknown;同时也未对请求做任何缓存处理;通常情况推荐使用以下的包装函数:get / post / head / del / put / patch

get\<T>(url: string, typeGard?: TypeGuardParam\<T> | null, options?: IRequestOptions)

  • url: 请求的资源路径,如果需要跳过全局 baseURL 设置,可以传递完整 url 地址
  • typeGuard: 可选类型守卫,如果设置为 null,则返回的内容为 unkonwn,否则就是经过类型守卫检查后的类型化数据,推荐传递类型守卫
  • options: 可选请求配置参数

注意,get 请求默认设置 500ms 缓存,即对同一个请求(url+params)在 500ms 内不再重复发起请求,而是直接返回上一次的结果,其中 params 会从 options.params 以及 url 中查找

post(url: string, data: RequstBody, typeGard?: TypeGuardParam\<T> | null, options?: IRequestOptions)

  • url: 请求的资源路径,如果需要跳过全局 baseURL 设置,可以传递完整 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 (status === 401) {
      return "";
    }
    if (status === 404) {
      return `${method} ${url} 接口未定义`;
    }
    if (status >= 500 && status <= 599) {
      return "🚨 服务器错误,请稍候再试";
    }
    if (code === "Unknown") {
      return `⛔ ${url} 出现未知错误,请稍候再试`;
    }
    return message;
  },
});

Sentry 信息上报

import { captureException } from "@sentry/vue";

setGlobalConfig({
  errorHandler({ status, sentryError, sentryTags, sentryExtra }) {
    if (status === 401) {
      return;
    }
    captureException(sentryError, {
      tags: sentryTags,
      extra: sentryExtra,
    });
  },
});
2.0.3

12 months ago

2.0.2

12 months ago

2.0.5

12 months ago

2.0.4

12 months ago

1.7.10

1 year ago

2.0.7

10 months ago

1.7.11

1 year ago

2.0.6

12 months ago

2.0.9

10 months ago

2.0.8

10 months ago

2.0.1

1 year ago

2.0.0

1 year ago

3.2.0

7 months ago

1.7.9

1 year ago

1.7.8

1 year ago

3.1.3

7 months ago

3.1.2

7 months ago

3.1.1

7 months ago

3.1.0

7 months ago

3.1.5

7 months ago

3.1.4

7 months ago

3.0.4

7 months ago

3.0.3

8 months ago

3.0.2

8 months ago

3.0.1

8 months ago

3.0.0

9 months ago

2.1.2

10 months ago

2.1.1

10 months ago

2.0.10

10 months ago

2.1.0

10 months ago

1.7.7

1 year ago

1.7.6

1 year ago

1.7.5

1 year ago

1.7.4

1 year ago

1.7.3

2 years ago

1.7.2

2 years ago

1.7.1

2 years ago

1.7.0

2 years ago

1.6.7

2 years ago

1.6.6

2 years ago

1.6.5

2 years ago

1.6.4

2 years ago

1.6.3

2 years ago

1.6.2

2 years ago

1.6.1

2 years ago

1.6.0

2 years ago

1.5.6

2 years ago

1.5.5

2 years ago

1.5.4

2 years ago

1.5.3

2 years ago

1.5.2

2 years ago

1.5.1

2 years ago

1.5.0

2 years ago

1.4.1

2 years ago

1.4.0

2 years ago

1.3.3

2 years ago

1.3.2

2 years 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