一个基于 Axios 封装 HTTP 类库
源代码 kz-http
使用方法
npm 安装
npm i kz-http -S
请求
import Http from 'kz-http'
let http = new Http()
http.get('https://www.example.com').then(res => {
console.log(res)
})
能解决什么
axios 明明那么好用,为啥又要基于 axios 重新造一个轮子。首先不得否认的是 axios 确实好用,Github 能斩获近 90k 的 star,且基本已成为前端作为数据交互的必备工具。但是它对我所使用的环境下还是存在一定的问题,也就是我为什么要重新造一个轮子。
Node 环境下无法自动封装 Set-Cookie
如果 axios 是运行在浏览器那还好说,就算你无论怎么请求,浏览器都会自动将你的所有请求中的响应包含 set-cookie 参数,提供给下一次同域下的请求。但是,Node 环境并不是浏览器环境,在 Node 环境中运行并不会自动保存 Cookie,还需要手动保存,并将 Cookie 添加至协议头给下一个请求。(如果是 Python 的话,request 有个 session 方法可以自动保存 cookie,十分方便)
一开始我是自行封装,将响应中的 set-cookie 全都存在实例对象 http.cookies 上,但封装的不彻底,如果有的网站
间请求存在跨域,那么会将携带不该属于该域下的 Cookies。于是乎,我在 github 仓库找到了一个库可达到我的目的
3846masa/axios-cookiejar-support: Add tough-cookie support to axios. (github.com)
具体安装可以直接点击链接查看,这里贴下我之前的封装代码
const tough = require('tough-cookie');
const axiosCookieJarSupport = require('axios-cookiejar-support').default;
axiosCookieJarSupport(axios);
class Http {
public cookieJar;
public instance: AxiosInstance;
construction() {
this.cookieJar = new tough.CookieJar(null, { allowSpecialUseDomain: true });
this.instance = axios.create({
jar: this.cookieJar,
ignoreCookieErrors: false,
withCredentials: true,
});
}
}
这样 axios 就会自动将响应中的 set-cookie 封装起来,供下次使用
但是正是由于导入了这个包,导致每次请求都需要处理,就会导致请求速度变慢,实测大约是在 100ms 左右,同时导入这个包之后,实例化的对象都将会携带对应 cookies,想要删除又得对应 Url,于是决定自行封装相关代码可查看 request 方法,实测下来大约有 10ms 左右的差距(前提都通过创建实例来请求),不过有个缺陷,我封装的代码是不进行同源判断的,如何你当前站点请求的是 api1.test.com,获取到 cookie1,那么请求 api2.test.com 的时候也会将 cookie1 携带,这边不做判断是不想在请求的时候耗费时间,比如网页与手机协议,一般这种情况建议实例化两个对象,如
let http_api1 = new Http()
let http_api2 = new Http()
请求失败无法自动重试
在高并发的情况下,偶尔会出现请求超时,请求拒绝的情况,但是默认下 axios 是不支持自动重试请求的,不过可以借助插件axios-retry
来达到这个目的
const axiosRetry = require('axios-retry')
class Http {
constructor(retryConfig?) {
this.instance = axios.create()
if (retryConfig) {
axiosRetry(this.instance, {
retries: retryConfig.retry, // 设置自动发送请求次数
retryDelay: (retryCount) => {
return retryCount * retryConfig.delay // 重复请求延迟
},
shouldResetTimeout: true, // 重置超时时间
retryCondition: (error) => {
if (axiosRetry.isNetworkOrIdempotentRequestError(error)) {
return true
}
if (error.code == 'ECONNABORTED' && error.message.indexOf('timeout') != -1) {
return true
}
if (['ECONNRESET', 'ETIMEDOUT'].includes(error.code)) {
// , 'ENOTFOUND'
return true
}
return false
},
})
}
}
}
这边判断重新发送请求条件是连接拒绝,连接重置,和连接超时的情况。