Axios 是一个非常流行的前端 HTTP 请求库,它可以方便地发起 HTTP 请求并处理响应结果。Axios 不仅支持基本的 GET、POST 等请求方式,还提供了丰富的配置项和拦截器机制,使得开发者可以对请求和响应进行全方位的控制和定制。
在本文中,我们将通过手写 Axios 的方式来学习请求拦截器和响应拦截器的实现。具体来说,我们将会实现一个基于 XMLHttpRequest 对象的 Axios 类,并在其中添加请求拦截器和响应拦截器。
实现 Axios 类
首先,我们需要创建一个 Axios 类,并在其中定义一些属性和方法。具体来说,我们需要:
定义一个拦截器对象,用于存储请求拦截器和响应拦截器;
定义一个 request 方法,用于发送 HTTP 请求;
定义一个 get 方法,用于发送 GET 请求;
定义一个 post 方法,用于发送 POST 请求。
下面是基本的 Axios 类的实现代码:
class Axios {
constructor() {
// 定义拦截器对象
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager(),
};
}
// 发送请求的方法
request(config) {
// 创建一个 Promise 对象
const promise = new Promise((resolve, reject) => {
// 创建一个 XMLHttpRequest 对象
const xhr = new XMLHttpRequest();
// 处理响应结果的函数
const handleResponse = () => {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status === 200) {
// 成功响应,执行响应拦截器后解析响应数据并返回
let response = xhr.response;
response = this.interceptors.response.run(response);
resolve(response);
} else {
// 响应失败,直接 reject
reject(xhr.statusText);
}
};
// 设置请求方式和 URL
xhr.open(config.method, config.url);
// 设置请求头
if (config.headers) {
Object.keys(config.headers).forEach((key) => {
xhr.setRequestHeader(key, config.headers[key]);
});
}
// 发送请求
xhr.send(config.data);
// 监听状态变化
xhr.onreadystatechange = handleResponse;
});
return promise;
}
// 发送 GET 请求的方法
get(url, config) {
config = Object.assign({}, config, { method: 'GET', url });
return this.request(config);
}
// 发送 POST 请求的方法
post(url, data, config) {
config = Object.assign({}, config, { method: 'POST', url, data });
return this.request(config);
}
}
在上述代码中,我们定义了一个 Axios 类,其中:
构造函数中定义了一个拦截器对象,包括请求拦截器和响应拦截器;
request 方法用于发送 HTTP 请求,它接收一个配置对象作为参数,并返回一个 Promise 对象;
get 方法用于发送 GET请求,它接收一个 URL 和配置对象作为参数,并返回一个 Promise 对象;
post 方法用于发送 POST 请求,它接收一个 URL、请求体数据和配置对象作为参数,并返回一个 Promise 对象。
现在我们已经定义了 Axios 类的基本骨架,接下来我们需要实现请求拦截器和响应拦截器的功能。
实现请求拦截器和响应拦截器
请求拦截器和响应拦截器是 Axios 提供的两个非常重要的功能,它们允许我们在请求和响应过程中执行一些自定义的逻辑,例如:
统一设置请求头;
统一处理请求错误;
统一处理响应错误;
统一处理响应数据。
在 Axios 中,我们可以通过拦截器对象来实现请求拦截器和响应拦截器。拦截器对象包含两个属性:request 和 response。其中,request 属性包含了请求拦截器,response 属性包含了响应拦截器。
每个拦截器对象都包含了一个 run 方法,该方法接收一个参数,并将参数传递给所有注册在该拦截器对象上的拦截器函数。
现在我们来实现拦截器对象和拦截器函数。
class InterceptorManager {
constructor() {
// 定义拦截器数组
this.interceptors = [];
}
// 注册拦截器函数
use(fulfilled, rejected) {
this.interceptors.push({ fulfilled, rejected });
return this.interceptors.length - 1;
}
// 执行拦截器函数
run(data) {
let result = data;
this.interceptors.forEach((interceptor) => {
const { fulfilled, rejected } = interceptor;
if (fulfilled) {
result = fulfilled(result);
}
});
return result;
}
}
在上述代码中,我们定义了一个 InterceptorManager 类,其中:
构造函数中定义了一个拦截器数组;
use 方法用于注册拦截器函数,它接收两个参数:fulfilled 和 rejected,分别表示成功拦截器和失败拦截器。它返回一个拦截器函数的索引,可以用于后续的拦截器注销;
run 方法用于执行拦截器函数,它接收一个参数,并将参数传递给所有注册在该拦截器对象上的拦截器函数,最终返回拦截器函数的结果。
现在我们已经实现了拦截器对象和拦截器函数,接下来我们将它们与 Axios 类结合起来,实现请求拦截器和响应拦截器的功能。
class Axios {
constructor() {
// 定义拦截器对象
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager(),
};
}
// 发送 GET 请求
get(url, config) {
return this._requestMethodWithoutData("get", url, config);
}
// 发送 POST 请求
post(url, data, config) {
return this._requestMethodWithData("post", url, data, config);
}
// 发送 PUT 请求
put(url, data, config) {
return this._requestMethodWithData("put", url, data, config);
}
// 发送 PATCH 请求
patch(url, data, config) {
return this._requestMethodWithData("patch", url, data, config);
}
// 发送 DELETE 请求
delete(url, config) {
return this._requestMethodWithoutData("delete", url, config);
}
}
// 创建 axios 实例
const axios = new Axios();
// 导出 axios 实例
export default axios;
在上述代码中,我们对 Axios 类进行了修改,主要增加了以下功能:
在构造函数中定义了 request 和 response 拦截器对象;
定义了 interceptor 和 eject 方法,用于注册和注销拦截器;
在 _request 方法中使用拦截器对象的 run 方法,分别执行 request 和 response 拦截器中的拦截器函数;
重写了 GET、POST、PUT、PATCH 和 DELETE 方法,使用 _requestMethodWithData 和 _requestMethodWithoutData 方法来统一处理请求参数;
创建了一个 axios 实例,并导出。
现在我们已经完成了 axios 的基本实现,并且增加了请求拦截器和响应拦截器的功能。
使用手写的 axios
我们可以使用手写的 axios 发送 HTTP 请求,使用方式与原生的 axios 差不多,例如:
import axios from "./axios";
// 注册请求拦截器
axios.interceptors.request.use(
(config) => {
// 统一设置请求头
config.headers["X-Requested-With"] = "XMLHttpRequest";
return config;
},
(error) => {
// 统一处理请求错误
console.error("请求错误:", error);
return Promise.reject(error);
}
);
// 注册响应拦截器
axios.interceptors.response.use(
(response) => {
// 统一处理响应数据
if (response.status === 200) {
return response.data;
} else {
return Promise.reject(response);
}
},
(error) => {
// 统一处理响应错误
console.error("响应错误:", error);
return Promise.reject(error);
}
);
// 发送 GET 请求
axios.get("https://jsonplaceholder.typicode.com/todos/1").then((data) => {
console.log(data);
});
// 发送 POST 请求
axios.post("https://jsonplaceholder.typicode.com/posts", {
title: "foo",
body: "bar",
userId: 1,
}).then((data) => {
console.log(data);
});
在上述代码中,我们通过 axios.interceptors.request.use 和 axios.interceptors.response.use 方法注册了请求拦截器和响应拦截器。我们可以在拦截器中统一设置请求头、统一处理请求错误、统一处理响应数据、统一处理响应错误等操作。
然后我们通过 axios.get 和 axios.post 方法发送了 GET 和 POST 请求,并在 then 方法中获取响应数据,并进行了打印输出。这里的使用方式与原生的 axios 差不多,但是我们可以使用我们手写的 axios,并在其中添加我们需要的功能,例如请求拦截器和响应拦截器。
总结
本文通过手写一个简化版的 axios 实现了请求拦截器和响应拦截器的功能,并且介绍了 axios 的基本用法。在实现过程中,我们首先定义了一个 Axios 类,并在其中实现了基本的 HTTP 请求方法。然后,我们增加了请求拦截器和响应拦截器的功能,并在其中实现了拦截器对象的注册、注销和执行方法。
手写一个 axios 的过程虽然比较繁琐,但是它可以帮助我们更深入地理解 axios 的实现原理,以及如何使用 axios 发送 HTTP 请求。同时,我们还可以在其中添加我们需要的功能,例如请求拦截器和响应拦截器,以便于我们统一处理请求和响应数据,提高开发效率。