目录
本文介绍axios的特点,简单的操作,可配置属性,统一的封装(适合大型项目)以及一些高级操作,比如取消请求,请求重试,上传文件,xsrf攻击防御。
前言
作为一个前端码农,应该明白 Ajax(Asynchronous JavaScript and XML,即异步网络请求)技术的出现对现在前端技术发展是巨大的促进的。但是对于不同的浏览器对ajax的支持并不同,需要统一兼容api,而且 ajax 调用接口很容易形成回调地狱。
因此 axios 横空出世,接下来我将介绍 axios 的特点和用户,并有相应的例子。
「Axios特点」
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
「Axios 基本操作」
npm install axios
- GET
function get(url) {
return axios.get(url, {
method: "get",
baseURL: "http://localhost:3001",
params: {},
timeout: 1000,
timeoutErrorMessage: "请求超时",
});
}
正常返回
请求超时
- POST
function post(url, data) {
return axios.post(url, data, {
baseURL: "http://localhost:3001",
timeout: 1000,
timeoutErrorMessage: "请求超时",
});
}
- 浏览器端
- 服务端
「Axios 配置」
- AxiosRequestConfig请求属性配置
config | default | Remark |
url | 请求路径 | |
baseURL | 基础请求路径 | |
method | 请求方法 | |
params | 请求参数 | |
paramsSerializer |
| |
data | 请求体 | |
headers | 头部属性 | |
timeout | 超时设置 | |
timeoutErrorMessage | 超时提示语 | |
responseType | json | 响应类型 |
responseEncoding | utf8 | 响应编码格式 |
withCredentials | 跨域请求时是否需要使用凭证 | |
maxContentLength | 允许的响应内容的最大尺寸 | |
maxRedirects | 5 | 在 node.js 中 follow 的最大重定向数目 |
httpAgent | 在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理 | |
httpsAgent | 在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理 | |
cancelToken |
| |
xsrfCookieName | XSRF-TOKEN | |
xsrfHeaderName | X-XSRF-TOKEN | |
transformRequest |
只能用在 ‘PUT’, ‘POST’ 和 ‘PATCH’ 这几个请求方法 | |
transformResponse |
| |
validateStatus | ||
onUploadProgress | 上传进度回调函数 | |
onDownloadProgress | 下载进度回调函数 |
「Axios 统一封装」
注: 当我们开发前后台分离项目时,我们会调用很多服务,如果每次都基础方式调用,不方便管理而且存在代码重复,也不方便公共操作的实现,比如每次调用服务前组装服务请求头信息等等
- 方式一
import axios from "axios";
const config = {
baseURL: "http://localhost:3001",
timeout: 50000,
};
// 创建axios实例
var instance = axios.create();
//超时设置
instance.defaults.timeout = config.timeout;
instance.defaults.baseURL = config.baseURL;
//设置form头属性
instance.defaults.headers.post["Content-Type"] =
"application/x-www-form-urlencoded;charset=UTF-8";
//request 拦截器,统一处理请求对象
instance.interceptors.request.use(
(config) => {
// 统一请求头处理
const token = "token";
token && (config.headers.Authorization = token);
return config;
},
(error) => {
return Promise.error(error);
}
);
//response 拦截器,统一处理响应对象
instance.interceptors.response.use(
(response) => {
if (response.status === 200) {
return Promise.resolve(response.data);
} else {
return Promise.reject(response);
}
},
// 服务器状态码不是200的情况
(error) => {
if (error.response && error.response.status) {
switch (error.response.status) {
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
console.info("跳转登录页");
break;
// 403 token过期
// 登录过期对用户进行提示
// 清除本地token和清空vuex中token对象
// 跳转登录页面
case 403:
console.info("跳转登录页登陆过期");
// 清除token
localStorage.removeItem("token");
// store.commit('loginSuccess', null);
// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
console.info("跳转过期");
}, 1000);
break;
// 404请求不存在
case 404:
console.info("404");
break;
// 其他错误,直接抛出错误提示
default:
console.info("其他错误");
}
return Promise.reject(error.response);
}else {
return Promise.reject(error.message);
}
}
);
export default instance;
请求效果
- 方式二
import axios from "axios";
import { cookie } from "levenx-utils";
const defaultConfig = {
baseURL: {
normal: "http://localhost:3001",
mock: "https://www.easy-mock.com/mock/5d2340fc05020b09e165b709/bookService",
},
timeout: 5000,
method: "get",
silent: true,
isMock: false,
};
export default class Axios {
static request(options) {
options = Object.assign({}, defaultConfig, options);
const {
isMock,
baseURL,
timeout,
url,
method,
params,
data,
//不显示loading
silent,
} = options;
let loading;
if (!silent) {
loading = document.getElementById("ajaxLoading");
if (loading) {
loading.style.display = "block";
}
}
return axios({
url,
method,
baseURL: isMock ? baseURL["mock"] : baseURL["normal"],
timeout,
params,
data,
headers: {
Authorization: cookie.get("authorization"),
},
})
.then((response) => {
//设置token
if (response.headers.authorization) {
cookie.set("authorization", response.headers.authorization);
}
if (!silent) {
loading = loading || document.getElementById("ajaxLoading");
if (loading) {
loading.style.display = "none";
}
}
if (response.status === 200) {
let res = response.data;
return res;
} else {
return response.data;
}
})
.catch((error) => {
if (!silent) {
loading = loading || document.getElementById("ajaxLoading");
if (loading) {
loading.style.display = "none";
}
}
alert(`提示:${error.message}`);
});
}
static get(url, options = {}) {
return Axios.request(Object.assign({}, options, { url }));
}
static post(url, data, options = {}) {
return Axios.request(
Object.assign({}, options, { url, data, method: "post" })
);
}
}
请求效果
「Axios 高级玩法」
- 取消请求
import axios from "axios";
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
Axios.post(
"/levenx/common/post",
{
name: "levenx",
},
{ cancelToken: source.token }
)
.then((res) => {
debugger;
})
.catch((err) => {
alert(err);
return err;
});
debugger;
source.cancel("取消请求");
- 请求重试
对于一个应用来说,如果由于某一个接口请求失败就导致应用崩溃或者给用户提示系统异常,但是我们又没办法避免由于服务不稳定导致的偶尔接口请求失败的情况。
此时可以考虑引入 「请求重试」 功能。
- 引入 axios-retry
npm install axios-retry
- 小试牛刀
import axios from "axios";
import axiosRetry from "axios-retry";
export function retry() {
axiosRetry(axios, { retries: 2 });
return axios.get("http://localhost:3001/levenx/common");
}
由于本地后端服务没有开启,重试的参数retries设置为2,所有发起了3次接口请求。
- 文件上传
// 上传方法
function upload(url, file) {
let config = {
headers: { "Content-Type": "multipart/form-data" },
baseURL: "http://localhost:3001",
};
let data = new FormData();
data.append("file", file);
return axios.post(url, data, config);
}
//react.js
class Login extends Component {
async componentDidMount() {}
//上传按钮触发上传动作
upload = async () => {
const { file } = this.state;
let result = await upload("/levenx/common/upload", file);
};
render() {
return (
<div>
<input
type='file'
onChange={(e) => {
// 监听文件变化
this.setState({ file: e.currentTarget.files[0] });
}}
/>
<button onClick={this.upload}>上传</button>
</div>
);
}
}
备注:上传图片后端代码可以参考之前写的博客。ajax图片上传功能实现(点击,拖拽,粘贴)Koa服务端
- XSRF 防御
CSRF 攻击的原理,就是利用由于浏览器的同源策略对 script 、img、video、audio等嵌入资源不做限制的行为进行跨站请求伪造的。 具体的XSS、CSRF攻击会单独记录,敬请关注。
// xsrf 配置
function get(url) {
return axios.get(url, {
method: "get",
baseURL: "http://localhost:3001",
params: {},
timeout: 1000,
timeoutErrorMessage: "请求超时",
//xsrf
withCredentials: true,
xsrfCookieName: "XSRF-TOKEN",
xsrfHeaderName: "X-XSRF-TOKEN",
});
}
//服务端验证
static async home(ctx) {
const header = ctx.header;
const cookie = ctx.cookies;
console.log("x-xsrf-token:", header["x-xsrf-token"]);
console.log("XSRF-TOKEN", cookie.get("XSRF-TOKEN"));
return {
code: 0,
message: "首页",
};
}
服务端效果图
判断是否存在xsrf攻击
判断header中 header[“x-xsrf-token”] 与 cookie.get(“XSRF-TOKEN”) 是否存在并且相等。 比如<img src=“http://localhost:3001/levenx/xsrf”/>也会发起服务,但是header属性并不会存在 x-xsrf-token。
模拟xsrf攻击
<img src='http://localhost:3001/levenx/common' />
<img src=‘http://localhost:3001/levenx/common’ />, cookie可以读取,但是header中缺少属性,从而可以防御xsrf。
axios提供快捷的方式设置 header 属性,如开局代码展示。
- 文件上传进度条onUploadProgress
function upload(url, file) {
let config = {
headers: { "Content-Type": "multipart/form-data" },
baseURL: "http://localhost:3001",
//上传进度条处理
onUploadProgress: ({ loaded, total }) => {
console.log("loaded:", loaded);
console.log("total:", total);
console.log("百分比:", (loaded / total) * 100 + "%");
},
};
let data = new FormData();
data.append("file", file);
return axios.post(url, data, config);
}