一个基于 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
},
})
}
}
}
这边判断重新发送请求条件是连接拒绝,连接重置,和连接超时的情况。
配置拦截器
有时候一个网站的协议是这样的,每一条 Post 都自动将所有参数进行拼接,然后进行 MD5 加密,并添加为 sign 参数,于是,不得不给每一条请求都进行这样的操作,那么有没有什么能在每次请求的时候,都自动的对参数进行 MD5 加密。如果使用过 axios 来配置过 JWT 效验,那自然就会熟悉给每条请求协议头都携带 JWT 数值 。同样的,这里的加密例子同样使用,具体配置实例对象 http 的请求拦截器即可,如
let http = new Http()
// axios实例instance是公开的
http.instance.interceptors.request.use(
config => {
// 执行每条请求都要处理的操作
return config
},
error => {},
)
同样的,响应拦截器也同理,例如请求返回的响应都进行加密处理,那么就可以通过响应拦截器进行统一解密,这里就不做过多描述,具体场景具体分析。
封装一些常用方法
比如设置伪造 IP(setFakeIP),自动补全 referer 和 orgin 参数,禁止重定向等等,更详细的查看源码便可
发布 npm 包
如果要让别人使用的话,总不可能让他去下载源码然后编译吧,这里就借助 npm。
在使用 npm 之前,请先使用npm install -g npm@latest
升级为最新版,否则可能会提示 ERR! 426 Upgrade Required。原文 The npm registry is deprecating TLS 1.0 and TLS 1.1 | The GitHub Blog
创建 npm 账号,创建 package.json
{
"name": "kz-http",
"version": "0.1.0",
"description": "An HTTP class library based on axios",
"main": "dist/index.js",
"scripts": {
"build": "tsc"
},
"author": "kuizuo",
"license": "ISC",
"dependencies": {
"axios": "^0.21.1",
"axios-retry": "^3.1.9"
},
"devDependencies": {
"typescript": "^4.3.5"
},
"repository": {
"type": "git",
"url": "git+https://github.com/kuizuo/kz-http.git"
},
"keywords": ["node", "axios", "http"]
}
然后通过npm login
登录 npm 账号,接着输入npm publish --access public
发布即可
发布的是要注意以下几点
-
如果 npm 镜像必须是官方的,否则无法登录,镜像还原
npm config set registry https://registry.npmjs.org/
查看镜像配置地址
npm get registry
-
如果包有重名,那么就无法发布,就必须要要改名
-
邮箱必须要验证(会接受一条下图邮箱),不然就会发布失败
-
请勿随意删包,否则同名的包将需要 24 小时后才能发布(亲测)
npm ERR! 403 403 Forbidden - PUT http://registry.npmjs.org/kz-http - kz-http cannot be republished until 24 hours have passed.
发布完成后,别人只需要通过npm i kz-http
就可成功将模块下载至本地 node_modules 文件夹下