还在用Axios?来试试这个基于fetch的现代请求库——Ky

背景

最近做了个AI大语言模型的前端页面,类似ChatGPT的那种,其中用到了流式传输的功能,即:

前端发起HTTP请求,服务端响应的HTTP HeaderConent-Type的值为text/event-stream,这种传输方式称之为SSE (Server-Sent Events)

这是一个流式传输的数据,服务端可以一部分一部分的发送到前端,前端也可以一部分一部分的展示到页面中,就像这样:

(自行脑补ChatGPT的聊天回复)

不必像一般的HTTP请求那样等待所有数据下载完成再展示给用户,可以边传输边展示,用户体验较好。

使用浏览器内置的fetchAPI可以轻松实现SSE流式传输,具体实现方式可以自行搜索,不是本文的重点内容。

但由于项目中使用了axios作为请求库,就需要通过axios来实现,但经过查阅axios相关的文档、博客、GitHub issue 等,发现axios对其的支持不是太好,实现起来较为繁琐,因为axios的底层是基于XHR的,相对来说已经是一个比较古老的API了。

所以萌生了一个想法,为何不找一个基于fetch的现代请求库来替代axios呢?毕竟都2024年这个时候了,fetch的兼容性已经非常好了。

经过一系列的搜寻和对比,发现了这个十分优秀的请求库------ky

Ky 简介

Ky是一个基于浏览器Fetch API的,小型优雅的HTTP客户端,具有以下特点:

  • 零依赖,大小仅3.3K
  • 跨运行时,支持在浏览器、Node.js、Deno 等不同的运行环境中使用,理论上应该可以运行在任意兼容fetchAPI的环境中
  • 支持创建实例 (类似axiosaxios.create)
  • 支持请求前后的Hooks钩子函数 (类似axiosinterceptors 拦截器)
  • 使用比fetch更加的简单便捷
  • 支持请求失败后自动重试的功能
  • 支持超时设置
  • 支持URL前缀
  • 更多...... 详见KyGitHub主页

截至本文撰写的时候,KyGitHub Star数已经高达11.3K

顺带一提,这个作者还写了一个专门用于Node.js的请求库叫做Got,其GitHub Star数更是高达13.9K,大佬恐怖如斯。

Ky 使用

安装

shell 复制代码
npm install ky --save

基础使用

有种axiosfetch结合体的感觉,哈哈哈

ts 复制代码
import ky from 'ky';

const data = await ky.get('https://example.com/').json();
const text = await ky.get('https://example.com/').text();
const data = await ky.post('https://example.com/', { json: { username: 'xxx' } }).json();

创建实例

这里有两点需要注意:

  1. Ky默认会在请求出错后自动重试2次,可以通过配置retry: 0为来关闭自动重试;
  2. Ky实例在配置了prefixUrl (URL前缀)之后,在发起请求时请求的路径不能再以"/"开头,否则会报错,详见下方代码
typescript 复制代码
const httpClient = ky.create({
  prefixUrl: 'https://example.com/',
  retry: 0, // 请求出错时 不自动重试 默认会自动重试2次
});

/** 正确的写法 最终会请求 https://example.com/api/user */
const data = httpClient.get('api/user').json();

/** 以下是错误的写法! Ky会直接报错,告诉你配置了prefixUrl后请求路径不能以"/"开头 */
const data = httpClient.get('/api/user').json();

Ky Hooks 钩子函数说明 (类似axios的拦截器)

KyHooksaxios的拦截器类似,都可以在请求之前或之后做一些统一的处理。

如果你不了解axiosKyHooksNode.js中的中间件(Middleware)思路上也有些类似。

以下是Hooks钩子函数列表:

  • beforeRequest 请求发起请求之前调用的Hook,可以在这里为请求统一添加用户的token,类似axios的拦截器interceptors.request.use

  • beforeRetry 请求重试之前调用的Hook

  • beforeError 请求抛出HTTPError之前调用的Hook,可以在这里统一处理所有的HTTPError,减少具体业务逻辑中的错误处理代码

  • afterResponse 请求响应之后调用的Hook,可以对响应的数据做一些统一处理

  • 更多...... 详见KyGitHub主页

配置实例的Hooks 进行全局统一的错误处理

在我们的日常开发中,请求的错误一般分为2种:

1. 业务上的错误,比如用户无权访问此接口、执行此操作之前必须先完成另一个操作等业务错误;

2. HTTP错误,如请求超时等。

针对第2种错误,我们可以直接在beforeError函数中进行处理;

针对第1种错误,就要分情况讨论了:

(1) 有些项目的业务错误,不会更改响应的HTTP状态码,后端会通过返回的JSON数据中的code字段来区分,正常情况code就为200,业务错误的话code就为其它值,这样就可以在afterResponse函数中匹配code的值进行统一的错误处理。

(2) 还有些项目的业务错误会直接更改响应的HTTP状态码,这种错误就和第2种错误类似了,都属于HTTP的错误,自然也都是在beforeError函数中进行统一的错误处理。

我现在所处的项目,就属于第(1)种情况,以下是一个针对这种情况的Ky全局统一错误处理小示例,如果你的项目是第(2)种情况也可以参考其思路:

ts 复制代码
const httpClient = ky.create({
  prefixUrl: 'https://example.com/',
  retry: 0, // 请求出错时 不自动重试 默认会自动重试2次
  hooks: {
    /** 请求发起之前 要执行的额外操作 */
    beforeRequest: [
      /** 每次发起请求前 获取用户的token 添加到请求header中 */
      (request, options) => {
        const token = getUserToken();
        token && request.headers.set('Authorization', token);
      },
    ],
    /** 服务器响应之后 请求调用的函数resolve之前 要执行的额外操作 */
    afterResponse: [
      /** 每次响应之后 根据响应的code进行错误处理 */
      async (request, options, response) => {
        // 忽略HTTP状态码不为 200 的响应
        if (response.status !== 200) {
          return;
        }

        // 判断 如果响应头的 Content-Type 不是 JSON 则忽略
        if (!response.headers.get('Content-Type')?.toLowerCase().includes('application/json')) {
          return;
        }

        // 响应头的 Content-Type 是 JSON 继续进行处理
        let data;
        try {
          data = await response.json();
        } catch (error) {
          // JSON解析失败
          console.error('响应数据解析为JSON时出错:', error);
          message.error('响应数据解析为JSON时出错!');
          throw error;
        }

        // code 不存在 或不是 number 的情况 则忽略
        if (typeof data.code !== 'number') {
          return;
        }

        // 根据服务端返回的 code 进行不同的处理
        switch (data.code) {
          case 200: {
            // 200 请求成功
            // 不执行任何操作
            break;
          }
          case 401: {
            // 401 未登录 或者 token 过期
            // 退出登录
            signOut();
            break;
          }
          default: {
            // 其他错误
            const errMsg = data.msg || '请求出错';
            // 提示错误信息
            message.warning(errMsg);
            // 抛出错误 请求发送方法调用的位置可以捕获到此错误
            throw new Error(errMsg);
          }
        }
      },
    ],
    /** HTTP 请求发生错误时 在抛出错误之前执行的操作
     * (afterResponse中抛出的错误不会导致这个钩子执行 只会在HTTPError时执行)
     */
    beforeError: [
      async error => {
        console.error('HTTPError', error);
        message.error(error.message);
        // 返回要抛出的 HTTPError 对象
        return error;
      },
    ],
  },
});

结语

一段时间使用下来,感觉Ky基本上是可以代替掉axios的,而且Ky可以直接返回fetchResponse对象,进而可以基于Response对象进行一些高级的操作,比如上文中提到的SSE流式传输,可以充分利用fetchAPI的优势,并且结合了axios的许多优点。

PS: 而且还支持跨运行时使用,浏览器、Node、Deno使用起来都无缝衔接 ~

相关推荐
码客前端2 分钟前
理解 Flex 布局中的 flex:1 与 min-width: 0 问题
前端·css·css3
Komorebi゛3 分钟前
【CSS】圆锥渐变流光效果边框样式实现
前端·css
工藤学编程15 分钟前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保15 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫16 分钟前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
欧阳天风24 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder28 分钟前
箭头函数和 this 绑定
linux·前端·javascript·typescript
郑州光合科技余经理28 分钟前
架构解析:同城本地生活服务o2o平台海外版
大数据·开发语言·前端·人工智能·架构·php·生活
沐墨染30 分钟前
大型数据分析组件前端实践:多维度检索与实时交互设计
前端·elementui·数据挖掘·数据分析·vue·交互
xkxnq34 分钟前
第一阶段:Vue 基础入门(第 11 天)
前端·javascript·vue.js