Skip to main content

Axios(网络请求库)

一、Axios 的定义

1、什么是 Axios

Axios 是一个基于 promise 网络请求库,可以用来发送 GET、POST 请求。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。

2、Axios 的优势

  • 可以在浏览器中发送 XMLHttpRequests
  • 可以在 node.js 发送 http 请求
  • 支持 Promise API
  • 可以拦截请求和响应
  • 可以转换请求数据和响应数据
  • 可以取消请求
  • 可以自动转换 JSON 数据
  • 客户端支持保护安全免受 XSRF 攻击

二、基本用法

1、Axios 的安装

使用 npm:

npm install axios

使用 bower:

bower install axios

使用 yarn:

yarn add axios

使用 jsDelivr CDN:

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

使用 unpkg CDN:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

2、支持的请求方法

  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.options(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])

3、发起 GET/POST 请求

3-1、发起 GET 请求

const axios = require('axios');

// 向给定ID的用户发起请求
axios.get('/user?ID=12345')
.then(function (response) {
// 处理成功情况
console.log(response);
})
.catch(function (error) {
// 处理错误情况
console.log(error);
})
.then(function () {
// 总是会执行
});

// 上述请求也可以按以下方式完成(可选)
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// 总是会执行
});

// 支持 async/await 用法
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
}

3-2、发起 POST 请求

axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

4、发起多个并发请求

function getUserAccount() {
return axios.get('/user/12345');
}

function getUserPermissions() {
return axios.get('/user/12345/permissions');
}

Promise.all([getUserAccount(), getUserPermissions()])
.then(function (results) {
const acct = results[0];
const perm = results[1];
});

三、传递请求配置

除了上面的基本用法外,可以通过向 axios 传递相关配置来创建请求,例如:

// 发起一个post请求
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});

下面是创建请求时可以用的配置 config 选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 GET 方法。

{
// 是用于请求的服务器 URL
url: '/user',
// 创建请求时使用的方法,默认为 get
method: 'get',
// baseURL 将自动加在 url 前面,设置以便为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// 在向服务器发送前,修改请求数据,只能用与 PUT, POST 和 PATCH 这几个请求方法
transformRequest: [function (data, headers) {
// 对发送的 data 进行处理,返回 Buffer、ArrayBuffer、FormData 或 Stream
return data;
}],
// 在传递给 then/catch 前,修改响应数据
transformResponse: [function (data) {
// 对接收的 data 进行任意转换处理
return data;
}],
// 即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// 即将与请求一起发送的 URL 参数,必须是个简单对象或 URLSearchParams 对象
params: {
ID: 12345
},
// 负责 params 序列化的函数
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// 作为请求体被发送的数据,仅适用 PUT、POST、DELETE 和 PATCH 请求方法
data: {
firstName: 'Fred'
},
// 指定请求超时的毫秒数,如果请求超时则请求被中断,默认为 0
timeout: 1000,
// 跨域请求时是否需要使用凭证,默认为 false
withCredentials: false,
// 自定义处理请求,返回一个 promise 并提供一个有效的响应
adapter: function (config) {
/* ... */
},
// auth HTTP Basic Auth
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
// 浏览器将要响应的数据类型,包括: arraybuffer、document、json(默认)、text、stream
responseType: 'json',
// 用于解码响应的编码,默认为 utf8
responseEncoding: 'utf8',
// xsrf token 的值,被用作 cookie 的名称,默认为 XSRF-TOKEN
xsrfCookieName: 'XSRF-TOKEN',
// 带有 xsrf token 值的 HTTP 请求头名称,默认为 X-XSRF-TOKEN
xsrfHeaderName: 'X-XSRF-TOKEN',
// 为上传处理进度事件
onUploadProgress: function (progressEvent) {
// 对原生进度事件的处理
},
// 为下载处理进度事件
onDownloadProgress: function (progressEvent) {
// 对原生进度事件的处理
},
// node.js 中允许的 HTTP 响应内容的最大字节数
maxContentLength: 2000,
// 允许的 HTTP 请求内容的最大字节数
maxBodyLength: 2000,
// 给定的 HTTP 状态码是 resolve 的条件,默认为 200 ~ 300
validateStatus: function (status) {
return status >= 200 && status < 300;
},
// node.js 中要遵循的最大重定向数,默认为 5,如果为 0 则不进行重定向
maxRedirects: 5,
// node.js 中使用的 UNIX 套接字,默认为 null
socketPath: null,
// node.js 中用于定义在执行 http 时使用的自定义代理
httpAgent: new http.Agent({ keepAlive: true }),
// node.js 中用于定义在执行 https 时使用的自定义代理
httpsAgent: new https.Agent({ keepAlive: true }),
// 定义代理服务器的主机名称和端口
proxy: {
protocol: 'https',
host: '127.0.0.1',
port: 9000,
// 表示 HTTP 基础验证应当用于连接代理,并提供凭据
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// 指定用于取消请求的 cancel token
cancelToken: new CancelToken(function (cancel) {
}),
}

点击查看多种调用 Axios 的形式及原理

四、创建 Axios 实例 create

可以使用自定义配置新建一个 axios 实例,并且可以在请求或响应被 then 或 catch 处理前拦截它们。

// http.js
import axios from 'axios'

// 创建实例时设置配置的默认值
var instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: { 'X-Custom-Header': 'foobar' }
});

// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
/**
1、比如添加 token 之类的请求头信息
2、添加每次请求 loading 等
*/
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么
/**
1、集中处理响应数据(如错误码处理)
*/
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});

export default instance

如何使用上面的 http.js 呢?

import http from 'xxx/http'

http({
method: 'POST',
url: '/user',
data: {
name: 'Leophen',
website: 'leophen.top'
}
}).then((response) => {
// 200 响应
}, (err) => {
// 500 响应
})

以下是可用的实例方法,指定的配置将与实例的配置合并:

  • axios#request(config)
  • axios#get(url[, config])
  • axios#delete(url[, config])
  • axios#head(url[, config])
  • axios#options(url[, config])
  • axios#post(url[, data[, config]])
  • axios#put(url[, data[, config]])
  • axios#patch(url[, data[, config]])
  • axios#getUri([config])

五、返回的响应信息

一个请求的返回包含以下信息:

{
// 服务器提供的响应
data: {},
// 来自服务器响应的 HTTP 状态码
status: 200,
// 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// 服务器响应头,可用方括号语法访问 response.headers['content-type']
headers: {},
// axios 请求的配置信息
config: {},
// 生成此响应的请求
// 在 node.js 中它是最后一个 ClientRequest 实例 (in redirects)
// 在浏览器中则是 XMLHttpRequest 实例
request: {}
}

当使用 then 时,将接收如下响应:

axios.get('/user/12345')
.then(function (response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});

使用 catch 时,或传入一个 rejection callback 作为 then 的第二个参数时,返回的错误信息将能够被处理。

六、配置默认值 defaults

Axios 可以设置一个默认的配置,该配置将在之后的每次请求中生效。

1、全局默认配置

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

2、实例中自定义默认值

// 创建实例时配置默认值
const instance = axios.create({
baseURL: 'https://api.example.com'
});

// 创建实例后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

3、配置优先级

配置(config)将按照优先顺序合并起来,首先的是在 lib/defaults.js 中定义的默认配置,其次是 defaults 实例属性的配置,最后是请求中 config 参数的配置。越往后面的等级越高,会覆盖前面的设置,举个例子:

// 使用库提供的默认配置创建实例,使用默认的超时配置 0
const instance = axios.create();

// 重写覆盖库中的超时默认值
// 现在,所有使用此实例的请求都将等待 2.5 秒,然后才会超时
instance.defaults.timeout = 2500;

// 重写覆盖此请求的超时时间,因为该请求需要很长时间
instance.get('/longRequest', {
timeout: 5000
});

七、拦截器 interceptors

在请求或响应被 then 或 catch 处理前拦截它们。

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});

如果稍后需要移除拦截器,可以这样:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/ });
axios.interceptors.request.eject(myInterceptor);

可以在自定义的 axios 实例中使用拦截器:

const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/ });

八、错误处理 catch

axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// 请求已经成功发起,但没有收到响应
// error.request 在浏览器中是 XMLHttpRequest 的实例
// error.request 在 node.js 中是 http.ClientRequest 的实例
console.log(error.request);
} else {
// 发送请求时出了点问题
console.log('Error', error.message);
}
console.log(error.config);
});

可以使用 validateStatus 配置选项自定义 HTTP 状态码的错误范围:

axios.get('/user/12345', {
validateStatus: function (status) {
return status < 500; // 当状态码小于 500 时视为错误
}
})

使用 toJSON 可以获取更多关于 HTTP 错误的信息:

axios.get('/user/12345')
.catch(function (error) {
console.log(error.toJSON());
});

九、取消请求 CancelToken

Axios 可以使用 cancel token 来取消请求,示例:

场景:给一个搜索框,每次输入字符都会调用接口,这时没办法知道哪个接口数据返回的是最后一次,只能取消之前发起的相同接口。

var CancelToken = axios.CancelToken;
var source = CancelToken.source();

axios.get('/user/12345', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

点击查看取消请求的原理

十、Axios 的封装

1、封装的目的

  • 设置请求头
  • 实现请求和响应拦截
  • 处理常见的错误信息
  • 集中式管理 API

2、初始化 Axios 实例

封装 Axios 可以使用 axios.create() 创建实例,以便后续相关配置:

// 创建 axios 请求实例
const serviceAxios = axios.create({
baseURL: "", // 基础请求地址
timeout: 10000, // 请求超时设置
withCredentials: false, // 跨域请求是否需要携带 cookie
});

3、设置请求拦截

发送请求时可能需要携带一些信息在请求头上,例如 token 等,所以需要将请求拦截下来,处理一些业务逻辑:

// 创建请求拦截
serviceAxios.interceptors.request.use(
(config) => {
// 是否开启 token 认证,如果开启则统一在 http 请求的 header 加上 token
// 这样后台根据 token 判断登录情况,此处 token 一般在用户完成登录后储存到 localStorage 中
if (serverConfig.useTokenAuthorization) {
config.headers["Authorization"] = localStorage.getItem("token"); // 请求头携带 token
}
// 设置请求头
if (!config.headers["content-type"]) { // 如果没有设置请求头
config.headers["content-type"] = "application/json"; // 默认类型
// 如果是 application/x-www-form-urlencoded
// config.headers["content-type"] = "application/x-www-form-urlencoded";
// config.data = qs.stringify(config.data); // 序列化,比如表单数据
}
return config;
},
(error) => {
Promise.reject(error);
}
);

上面使用的 qs 库用于序列化参数,POST 请求中的 Content-Type 常见的形式有以下 3 种:

  • Content-Type: application/json
  • Content-Type: application/x-www-form-urlencoded
  • Content-Type: multipart/form-data

主流的 application/json 形式给后端接口传参非常简单,直接放入 data 传递即可。但有时后端要求 Content-Type 必须以 application/x-www-form-urlencoded 形式,那么通过 {"id":"123","type":"aaa"} 的形式传递的参数后端是收不到的,必须对参数数据进行序列化处理才行,让它以普通表单形式 (键值对) 发到后端,而非 json 形式。

4、设置响应拦截

Axios 请求的返回结果包含了很多东西,而业务层通常只需要后端返回的数据,所以要设置响应拦截,在响应结果返回给业务层之前做一些操作:

// 创建响应拦截
serviceAxios.interceptors.response.use(
(res) => {
const data = res.data;
// 处理自己的业务逻辑,比如判断 token 是否过期等等
// 代码块
return data;
},
(error) => {
let message = "";
if (error && error.response) {
switch (error.response.status) {
case 302:
message = "接口重定向了";
break;
case 400:
message = "参数不正确";
break;
case 401:
message = "您未登录,或者登录已经超时,请先登录";
break;
case 403:
message = "您没有权限操作";
break;
case 404:
message = `请求地址出错: ${error.response.config.url}`;
break;
case 408:
message = "请求超时";
break;
case 409:
message = "系统已存在相同数据";
break;
case 500:
message = "服务器内部错误";
break;
case 501:
message = "服务未实现";
break;
case 502:
message = "网关错误";
break;
case 503:
message = "服务不可用";
break;
case 504:
message = "服务暂时无法访问,请稍后再试";
break;
case 505:
message = "HTTP 版本不受支持";
break;
default:
message = "异常问题,请联系管理员";
break;
}
}
return Promise.reject(message);
}
);

5、完整代码

src/http/index.js
import axios from "axios";
import serverConfig from "./config";
import qs from "qs";

// 创建 axios 请求实例
const serviceAxios = axios.create({
baseURL: serverConfig.baseURL, // 基础请求地址
timeout: 10000, // 请求超时设置
withCredentials: false, // 跨域请求是否需要携带 cookie
});

// 创建请求拦截
serviceAxios.interceptors.request.use(
(config) => {
// 是否开启 token 认证
if (serverConfig.useTokenAuthorization) {
config.headers["Authorization"] = localStorage.getItem("token"); // 请求头携带 token
}
// 设置请求头
if (!config.headers["content-type"]) { // 如果没有设置请求头
config.headers["content-type"] = "application/json"; // 默认类型
// 如果是 application/x-www-form-urlencoded
// config.headers["content-type"] = "application/x-www-form-urlencoded";
// config.data = qs.stringify(config.data); // 序列化,比如表单数据
}
console.log("请求配置", config);
return config;
},
(error) => {
Promise.reject(error);
}
);

// 创建响应拦截
serviceAxios.interceptors.response.use(
(res) => {
// 处理自己的业务逻辑,比如判断 token 是否过期等等
return res;
},
(error) => {
let message = "";
if (error && error.response) {
switch (error.response.status) {
case 302:
message = "接口重定向了!";
break;
case 400:
message = "参数不正确!";
break;
case 401:
message = "您未登录,或者登录已经超时,请先登录!";
break;
case 403:
message = "您没有权限操作!";
break;
case 404:
message = `请求地址出错: ${error.response.config.url}`;
break;
case 408:
message = "请求超时!";
break;
case 409:
message = "系统已存在相同数据!";
break;
case 500:
message = "服务器内部错误!";
break;
case 501:
message = "服务未实现!";
break;
case 502:
message = "网关错误!";
break;
case 503:
message = "服务不可用!";
break;
case 504:
message = "服务暂时无法访问,请稍后再试!";
break;
case 505:
message = "HTTP 版本不受支持!";
break;
default:
message = "异常问题,请联系管理员!";
break;
}
}
return Promise.reject(message);
}
);
export default serviceAxios;

除此之外,还可以根据业务场景做一些以下扩展:

  • 请求拦截里面针对 token 进行处理;
  • 响应拦截里面判断 token 是否过期等;
  • config/index.js 里面动态更改 baseURL
  • 在请求拦截里面根据业务场景修改请求头;
  • 在拦截里面设置全局请求进度条等。