@xiaohuohumax/x-fetch-request
TypeScript icon, indicating that this package has built-in type declarations

0.2.0 • Public • Published

x-fetch-request

Version License GitHub Actions Workflow Status

一个基于 fetch API 的简单、轻量级的 HTTP 请求库。

[!NOTE] 此项目由 octokit - request.js [MIT] 项目修改而来,移除了对 Github API 相关的支持, 只保留了通用的 fetch 功能。

🚀 快速开始

安装:

npm install @xiaohuohumax/x-fetch-request

使用:

// ESM / Typescript
import { request } from "@xiaohuohumax/x-fetch-request"
// or

// CommonJS
const { request } = require("@xiaohuohumax/x-fetch-request")

📖 基本用法

import { request } from "@xiaohuohumax/x-fetch-request"

const newRequest = request.defaults({
  baseUrl: "https://api.github.com",
  params: {
    owner: "octokit",
    repo: "request.js",
  }
})

// 请求 Hook 示例
newRequest.hook.before("request", (options) => {
  console.log("before request", options)
})

// 方法一
newRequest({
  url: "/repos/{owner}/{repo}/issues{?page,per_page}",
  method: "GET",
  params: {
    page: 1,
    per_page: 1,
  }
}).then(console.log).catch(console.error)

// 方法二
newRequest("GET /repos/{owner}/{repo}/issues{?page,per_page}", {
  params: {
    page: 1,
    per_page: 1,
  }
}).then(console.log).catch(console.error)

✅ 异步支持

import { request } from "@xiaohuohumax/x-fetch-request"

const newRequest = request.defaults({/* options */})

// 异步 Promise
newRequest({/* options */})
  .then(console.log)
  .catch(console.error)

// 同步 await
const response = await newRequest({/* options */})
console.log(response)

✅ Node.js

const { request } = require("@xiaohuohumax/x-fetch-request")

const newRequest = request.defaults({
  baseUrl: "https://example.com",
})

newRequest("/api/users")
  .then(console.log)
  .catch(console.error)

✅ 请求头

import { request } from "@xiaohuohumax/x-fetch-request"

const newRequest = request.defaults({
  /* XFetch options */
  headers: {
    // 全局请求头
  }
})

// 方式一:简单对象 key-value
newRequest({
  /* request options */
  headers: {
    // 局部请求头
  }
}).then(console.log).catch(console.error)

// 或者
// 方式二:Headers 对象
const headers = new Headers()
newRequest({
  /* request options */
  // 使用 Headers 对象
  headers
}).then(console.log).catch(console.error)

✅ URI模板

[!NOTE] URI 模板规则参考 RFC 6570 规则。

import { request } from "@xiaohuohumax/x-fetch-request"

const newRequest = request.defaults({/* options */})

// 用户设置模板规则
// owner, repo => {owner}, {repo}
// page, per_page => {?page,per_page}
// 结果如下:
// GET /repos/octokit/request.js/issues?page=1&per_page=1
newRequest("GET /repos/{owner}/{repo}/issues{?page,per_page}", {
  params: {
    owner: "octokit",
    repo: "request.js",
    page: 1,
    per_page: 1,
  }
}).then(console.log).catch(console.error)

// 用户未设置模板规则,那么额外的参数会使用默认规则
// 规则如下:
// 1. 数组:hobby => {hobby*} => hobby=1&hobby=2&hobby=3
// 2. 非数组:name => {name} => name=value
// 结果如下:
// GET /users?hobby=1&hobby=2&hobby=3
newRequest("GET /users", {
  params: {
    hobby: [1, 2, 3],
  }
}).then(console.log).catch(console.error)

// 其他扩展规则
// 自动将路径中的 :id 格式转换为 {id}格式
// 结果如下:
// GET /users/123
newRequest("GET /users/:id", {
  params: {
    id: 123,
  }
}).then(console.log).catch(console.error)

✅ 中断请求

import { request } from "@xiaohuohumax/x-fetch-request"

const abortController = new AbortController()
const newRequest = request.defaults({
  /* XFetch options */
  request: {
    signal: abortController.signal,
  }
})

setTimeout(() => abortController.abort(), 10)

newRequest(/* Request options */)
  .then(console.log)
  .catch(console.error) // AbortError

✅ 钩子方法

目前支持的钩子有:

  • request 请求钩子:通过 fetch API 发送请求
  • parse-options 处理请求参数钩子:将请求参数处理成功 fetch API 所需参数
  • parse-error 处理请求错误钩子:将 fetch API 错误处理成自定义错误
  • parse-response 处理响应数据钩子:将 fetch API 响应数据处理成自定义数据

大致流程如下:

// `request`
function request(options: RequestOptions): Promise<XFetchResponse<any>> {
  // `parse-options`
  const fetchOptions = await parseOptions(options)
  let fetchResponse: Response
  try {
    fetchResponse = await fetch(options.url, fetchOptions)
  }
  catch (error) {
    // `parse-error`
    throw await parseError({ error, options })
  }
  // `parse-response`
  return await parseResponse({ fetchResponse, options })
}

[!NOTE] 每个钩子都有 beforeaftererrorwrap 四个阶段,分别对应前、后、错误、包装阶段。

import { request } from "@xiaohuohumax/x-fetch-request"

const newRequest = request.defaults({/* XFetch options */})

// before
newRequest.hook.before("request", (options) => {
  console.log("before request", options)
})

// after
newRequest.hook.after("request", (response) => {
  console.log("after request", response)
})

// error
newRequest.hook.error("request", (error) => {
  console.log("error request", error)
})

// wrap
newRequest.hook.wrap("request", (request, option) => {
  console.log("wrap request before", request, option)
  const response = request(option)
  console.log("wrap request after", request, option)
  return response
})

✅ 使用代理

[!NOTE] Node 环境推荐使用 undici

import { request } from "@xiaohuohumax/x-fetch-request"
// import { ProxyAgent, fetch as undiciFetch } from 'undici'
import { fetch as undiciFetch } from "undici"
const newRequest = request.defaults({
  /* XFetch options */
  request: {
    fetch: (url: any, options: any) => {
      return undiciFetch(url, {
        ...options,
        // dispatcher: new ProxyAgent('https://...'),
      })
    },
  },
})

newRequest({
  /** request options */
}).then(console.log).catch(console.error)

✅ JSON数据

发送JSON数据:

import { request } from "@xiaohuohumax/x-fetch-request"
const newRequest = request.defaults({
  baseUrl: "https://api.github.com",
})

// 当 method 为 POST, PUT, PATCH, DELETE 且 body 为可序列化对象时,则会自动添加默认的 content-type 和 accept 请求头
// 如果需要禁用自动设置,可以设置 request.autoSetDefaultHeaders 为 false
// 默认请求头如下:
// content-type: application/json
// accept: application/json, text/plain, */*
newRequest({
  url: "/users/xiaohuohumax",
  method: "POST",
  // 也可以设置请求头覆盖默认值
  // headers: {
  //   "content-type": "...",
  //   "accept": "...",
  // },
  body: {
    name: "xiaohuohumax",
  }
}).then(console.log).catch(console.error)

// 当请求头包含 content-type: application/json 时,请求数据会自动序列化为 JSON 字符串
// 如果需要禁用自动序列化,可以设置 request.autoParseRequestBody 为 false
newRequest({
  url: "/users/xiaohuohumax",
  body: {
    name: "xiaohuohumax",
  },
  request: {
    autoParseRequestBody: false,
  }
}).then(console.log).catch(console.error)

// 若是以上默认处理逻辑不满足需求,可以利用 `parse-options` 钩子的 `wrap` 方法自定义处理逻辑
newRequest.hook.wrap("parse-options", (oldFunc, options) => {
  // 原来的处理逻辑
  // return oldFunc(options)
  // 自定义处理逻辑
  return {
    // 处理结果
  }
})

接收JSON数据:

responseType(响应体处理格式) 支持类型:

  • json:响应体会自动解析为 JSON 对象,即 fetch().json()
  • text:响应体会以文本形式返回,即 fetch().text()
  • blob:响应体会以二进制 Blob 形式返回,即 fetch().blob()
  • stream:响应体会以 fetch(ReadableStream) 流形式返回,即 fetch().body
  • formData:响应体会以 FormData 形式返回,即 fetch().formData()
  • arrayBuffer:默认值,响应体会以 ArrayBuffer 形式返回,即 fetch().arrayBuffer()
import { request } from "@xiaohuohumax/x-fetch-request"
const newRequest = request.defaults({
  baseUrl: "https://api.github.com",
})

// // 响应头包含 content-type: application/json 时,data 字段会自动解析为 JSON 对象
const { status, data } = await newRequest({/** request options */ })

// console.log(status, data) // 200, {...}

// 若是想指定响应体格式,可以设置 responseType 字段
const { status, data } = await newRequest({
  /** request options */
  request: {
    // [`json` | `text` | `blob` | `stream` | `arrayBuffer` | `formData`]
    // 默认为 undefined arraybuffer
    responseType: "text",
  }
})
// console.log(status, data) // 200, ""

// 若是响应体默认处理逻辑不满足需求,可以利用 `parse-response` 钩子的 `wrap` 方法自定义处理逻辑
newRequest.hook.wrap("parse-response", (oldFunc, { fetchResponse, options }) => {
  // 原来的处理逻辑
  // return oldFunc({ fetchResponse, options })
  // 还请注意其他参数
  return {
    // data: "custom data", // 自定义处理逻辑
  }
})

✅ 类型推断

import { request } from "@xiaohuohumax/x-fetch-request"
const newRequest = request.defaults({/* XFetch options */})

interface User {
  [key: string]: any
}

const { status, data } = await newRequest<User>({/** request options */})

console.log(status, data) // 200, User

✅ 请求超时

import { request, XFetchTimeoutError } from "@xiaohuohumax/x-fetch-request"
const newRequest = request.defaults({
  /* XFetch options */
  request: {
    timeout: 10
  }
})

// 使用全局超时配置
newRequest({
  /** request options */
})
  .then(console.log)
  .catch((error) => {
    if (error instanceof XFetchTimeoutError) {
      console.error("Request timed out")
    }
  })

// 单独设置超时
newRequest({
  /** request options */
  request: {
    timeout: 5
  }
})
  .then(console.log)
  .catch((error) => {
    if (error instanceof XFetchTimeoutError) {
      console.error("Request timed out")
    }
  })

✅ 异常处理

  • XFetchError - 所有XFetch异常的基类
    • XFetchRequestError - 请求异常(包含请求、响应等信息)
    • XFetchTimeoutError - 请求超时异常(包含请求信息)
import { request, XFetchError, XFetchRequestError, XFetchTimeoutError } from "@xiaohuohumax/x-fetch-request"
const newRequest = request.defaults({
  /* XFetch options */
})

newRequest({/** request options */})
  .then(console.log)
  .catch((error) => {
    if (error instanceof XFetchError) {
      if (error instanceof XFetchRequestError) {
        console.error("Request failed")
      }
      else if (error instanceof XFetchTimeoutError) {
        console.error("Request timed out")
      }
    }
    console.error(error)
  })

📄 License

MIT

最后:玩的开心 🎉🎉🎉🎉

Package Sidebar

Install

npm i @xiaohuohumax/x-fetch-request

Weekly Downloads

0

Version

0.2.0

License

MIT

Unpacked Size

48.5 kB

Total Files

6

Last publish

Collaborators

  • xiaohuohumax