Axios多环境适配:浏览器与Node.js双平台请求实现
<|begin▁of▁sentence|># 1. 前言
在上一篇文章中,我们实现了`axios`的基础功能,但到目前为止,我们都只是实现了一个很简单的`axios`,一个最最基础的`axios`,到目前为止,我们只实现了:
- 在`浏览器端`使用`XMLHttpRequest`对象通信
- 支持`Promise`API
- 支持请求和响应数据配置
- 支持请求和响应错误处理
但是,我们还有很多功能没有实现,比如:
- 支持`node.js`端发送请求
- 支持请求拦截器
- 支持响应拦截器
- 支持取消请求
- 等等
从这篇文章开始,我们就来把这些功能一一实现。
# 2. 需求分析
在之前的文章中,我们只实现了在`浏览器端`发送请求,这是因为`axios` 是一个基于 `Promise` 的 `HTTP` 请求库,可以用在浏览器和 `node.js` 中。它优秀的特性如:支持`Promise`API、拦截请求和响应、转换请求和响应数据、自动转换`JSON`数据、客户端支持防御`XSRF`等。所以,我们也要让我们的`axios`能够同时支持在浏览器端和`node.js`端发送请求。
那么,如何让我们的`axios`能够同时支持在浏览器端和`node.js`端发送请求呢?我们首先想到的肯定是要判断当前运行环境,如果是浏览器环境,我们就使用`XMLHttpRequest`对象实现发送请求;如果是`node.js`环境,我们就使用`node.js`内置的`http`模块实现发送请求。
所以,接下来,我们就先来实现让`axios`能够支持在`node.js`端发送请求。
# 3. 运行环境判断
我们想要判断当前运行环境,首先就要能够获取到当前运行环境,那么如何获取呢?
在浏览器环境中,全局对象是`window`,并且`window`对象上有`document`对象,但是在`node.js`环境中,全局对象是`global`,并且没有`document`对象,所以我们可以根据这一点来判断当前运行环境。
我们可以在`src/axios.ts`文件中添加一个辅助函数`getDefaultAdapter`来获取默认的请求发送装置:
```typescript
import { AxiosRequestConfig } from "./types";
import xhr from "./xhr";
function getDefaultAdapter(): any {
if (typeof XMLHttpRequest !== "undefined") {
// 浏览器环境
return xhr;
}
}
```
然后,在`axios`函数中,我们先获取默认的请求发送装置,然后使用该装置发送请求:
```typescript
function axios(config: AxiosRequestConfig): AxiosPromise {
const adapter = getDefaultAdapter();
return adapter(config);
}
```
OK,这样我们就实现了在浏览器环境中使用`xhr`装置发送请求,接下来,我们就来实现`node.js`环境中的请求发送装置。
# 4. 实现 node.js 端请求发送装置
在`node.js`环境中,我们使用`http`模块实现请求发送,我们在`src`目录下创建`adapter`目录,并在该目录下创建`http.ts`文件,在该文件中实现`node.js`端的请求发送装置。
## 4.1 创建 http.ts
`http.ts`文件内容如下:
```typescript
import { AxiosPromise, AxiosRequestConfig } from "../types";
import { buildURL } from "../helpers/url";
import { transformRequest } from "../helpers/data";
import { processHeaders } from "../helpers/headers";
export default function http(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const {
url,
method = "get",
data = null,
params,
headers,
responseType,
} = config;
const { http, https } = require("follow-redirects");
// 处理url
let fullUrl = buildURL(url, params);
// 处理请求头
let newHeaders = processHeaders(headers, data);
// 处理数据
let newData = transformRequest(data);
// 创建请求对象
const transport = fullUrl.startsWith("https") ? https : http;
// 创建请求
const req = transport.request(
fullUrl,
{
method: method.toUpperCase(),
headers: newHeaders,
},
(res: any) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
let responseData = null;
if (responseType === "text") {
responseData = res.read().toString();
} else {
responseData = res.read();
}
const response = {
data: responseData,
status: res.statusCode,
statusText: res.statusMessage,
headers: res.headers,
config,
request: req,
};
resolve(response);
} else {
reject(
new Error(`Request failed with status code ${res.statusCode}`)
);
}
}
);
// 处理错误
req.on("error", (err: any) => {
reject(err);
});
// 处理超时
req.setTimeout(0);
// 发送请求
if (newData) {
req.write(newData);
}
req.end();
});
}
```
代码说明:
- 首先,我们从`config`中解构出我们需要的属性,并且给`method`和`data`设置默认值;
- 然后,我们引入`node.js`中的`http`和`https`模块,这里我们使用第三方库`follow-redirects`,它可以自动重定向,并且同时支持`http`和`https`;
- 接着,我们处理`url`,将`params`参数拼接到`url`上;
- 然后,我们处理请求头,将`headers`中的属性名转换成首字母大写的形式;
- 然后,我们处理请求数据,将`data`转换成`JSON`字符串;
- 然后,我们根据`url`的协议来判断使用`http`还是`https`模块;
- 然后,我们创建请求,并监听响应事件,当响应状态码在 200 到 300 之间时,我们认为请求成功,将响应数据转换成字符串或者`Buffer`,并返回响应对象;否则,我们认为请求失败,返回错误信息;
- 然后,我们监听错误事件,当发生错误时,返回错误信息;
- 然后,我们设置超时时间为 0,表示不超时;
- 最后,我们发送请求,如果有请求数据,我们就将数据写入请求体,然后结束请求。
## 4.2 修改 getDefaultAdapter 函数
现在,我们已经实现了`node.js`端的请求发送装置,接下来,我们修改`getDefaultAdapter`函数,让它能够返回`node.js`端的请求发送装置。
```typescript
import { AxiosRequestConfig } from "./types";
import xhr from "./xhr";
import http from "./adapter/http";
function getDefaultAdapter(): any {
if (typeof XMLHttpRequest !== "undefined") {
// 浏览器环境
return xhr;
} else if (typeof process !== "undefined") {
// node.js环境
return http;
}
}
```
我们通过判断全局对象`process`是否存在来判断当前是否是`node.js`环境,如果存在,就返回`http`装置。
## 4.3 安装 follow-redirects
由于我们在`http.ts`中使用了第三方库`follow-redirects`,所以我们需要先安装它:
```bash
npm install follow-redirects --save
```
安装完成之后,我们就可以在`node.js`环境中使用`http`装置发送请求了。
# 5. 编写 demo
接下来,我们编写一个`demo`来测试一下我们的`axios`是否能够在`node.js`环境中发送请求。
我们在`examples`目录下创建`server.js`文件,在该文件中我们创建一个简单的`http`服务器,用来接收请求并返回响应。
`server.js`文件内容如下:
```javascript
const http = require("http");
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
data: "Hello World!",
})
);
});
server.listen(3000, () => {
console.log("Server is running on http://localhost:3000");
});
```
然后,我们在`examples`目录下创建`node.js`文件,在该文件中我们使用`axios`发送请求。
`node.js`文件内容如下:
```javascript
const axios = require("../../src/index");
axios({
url: "http://localhost:3000",
method: "get",
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
```
# 6. 运行 demo
首先,我们启动服务器:
```bash
node examples/server.js
```
然后,我们运行`node.js`文件:
```bash
node examples/node.js
```
运行之后,我们可以在控制台看到如下输出:

OK,我们可以看到,我们的`axios`已经能够在`node.js`环境中发送请求了,并且成功收到了响应。
# 7. 遗留问题
我们虽然已经实现了在`node.js`环境中发送请求,但是我们还存在一些问题:
- 我们没有处理`responseType`,在`node.js`环境中,我们只能处理`text`和`json`,不能处理`arraybuffer`和`blob`等;
- 我们没有处理`cancelToken`,在`node.js`环境中,我们无法取消请求;
- 我们没有处理`withCredentials`,在`node.js`环境中,我们无法设置`withCredentials`;
- 我们没有处理`auth`,在`node.js`环境中,我们无法设置`auth`;
- 我们没有处理`proxy`,在`node.js`环境中,我们无法设置`proxy`;
- 等等。
这些问题,我们会在后面的文章中一一解决。
# 8. 总结
在本篇文章中,我们让`axios`能够同时支持在浏览器端和`node.js`端发送请求。我们通过判断当前运行环境来选择使用不同的请求发送装置。在浏览器环境中,我们使用`XMLHttpRequest`对象发送请求;在`node.js`环境中,我们使用`http`模块发送请求。
至此,我们的`axios`已经能够在浏览器和`node.js`环境中发送请求了,但是我们还存在很多问题,这些问题我们会在后面的文章中一一解决。
最新文章
- 凸轮轴设计与轮胎花纹优化:提升车辆性能的关键要素
- 电动化与智能化双轮驱动:汽车产业迎来革命性变革
- 驾驶员疲劳与行车安全
- 新手必看:掌握发动机转速、安全车距与雾灯使用的驾驶技巧
- L4自动驾驶靠谱吗?激光雷达与传感器的技术瓶颈解析
- 发动机号码与VIN码:揭秘爱车的身份密码
- 智能汽车传感器网络:毫米波雷达与激光雷达如何重塑自动驾驶
- 2023汽车科技四大突破:智能驾驶与电动化引领行业变革
- 电动化与智能化并行:固态电池、激光雷达、AR-HUD引领汽车技术革命
- 交通事故理赔指南:必备材料与流程详解
- 长城脚下汽车驰骋
- 自动驾驶技术演进:从L2到L5的产业变革与未来展望
- 800V高压平台:电动汽车快充革命与未来技术突破
- 可回收电动汽车环保节能未来出行新选择
- 车上人员责任险保障乘客安全出行无忧
- 冷却液保养与方向盘握法:行车安全的两大关键细节
- 激光雷达与毫米波雷达驱动下的V2X车联网自动驾驶革命
- 智能座舱与固态电池:未来汽车的三大技术变革
- 新能源车主必看:充电桩安全使用与冬季保养全攻略
- 刹车失灵危险大!应急处理与预防保养全指南
