本文主要是写一下关于Axios的一些配置问题,每个人都有自己的一套标准,我现在就将自己比较常用的代码写下,也供被人做参考。
第三方包永远是最好用的,正如锅里面的永远是最香的。
Axios作为一个已经发布了n多次的包,自然不会有什么大的纰漏,所以就成为了我们最常用的请求包,而每个项目都会封装属于自己项目的一些东西,作为重中之重的Axios必须也封装一次。
最基本的配置就是服务器的链接以及设置一个超时的最大时间了。
import axios from 'axios';
//创建axios的实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API || "http://www.baidu.com",
// 超时设置
timeout: 10000
})
export default service;
一般来说,我们做出请求的时候,除去特别的情况,我们都想要后台返回的是json格式的字符串。
所以我们都会先去设置下header中的"content-type"为"application/json;charset=utf-8",以保证数据的格式是我们的预期
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
一般请款改下,我们的后台除了登录接口,其他的接口都需要一个token校验是否登录,但是由于token是在后台返回的,所以我们无法像content-type一样一开始就设置,应该在拦截器中做判断来设置。
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
return config
}, error => {
console.log(error)
Promise.reject(error)
})
上述的代码中,getToken方法是自己设置的获取token的方法,一般来说就是放在sessionStorage或者localStorage中。
需要注意的是,catch中,我们会将错误信息打印出来,个人不喜欢抛出错误,因为这儿也不需要显示什么错误了。
最主要的一点是Promise.reject出一个error,这样子就不会走到下一步
混前端的,防抖节流是必须掌握的,在做Api请求的时候,我们也要做下相关的防抖节流处理。而根据的依据自然是请求的API url以及它的参数,而get请求由于有幂等性,所以不需要做太多的限制。
存放上述操作的判断依据,我都喜欢放在session中,毕竟不会很大,不过为了防止数据量很大,我只会保存上一次的数据。
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
}
}
做完了请求的的拦截之后,就需要面对response的拦截了,第一步是对状态码的判断,但是我不觉得对数据的格式化更重要,尤其是我们请求文件的时候,返回一个文件的数据,有部分人会说是一个Blob,有部分人会说是一个ArrayBuffer,部分人不清楚这些是什么…
所以我就先写这个。
顺带一提,创建一个axios的response的interceptors error的catch如下,自己看。
service.interceptors.response.use(res => {
// 二进制数据则直接返回
if(res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer'){
return res.data
}
},
error => {
console.log('err' + error)
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
}
else if (message.includes("timeout")) {
message = "系统接口请求超时";
}
else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
401代表的是我们现在已经没有资格去访问后台服务了,这个时候,代表我们应该考虑下是否应该要重新登陆了(如果有登陆系统的话)。
不过这个还是得要一个开关来设置的,毕竟是凡事都有例外。
具体的如何跳转到登陆页面,要每个人的项目来设置,有可能还要传递某些参数。
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if(res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer'){
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true;
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
isRelogin.show = false;
store.dispatch('LogOut').then(() => {
location.href = '/index';
})
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({
message: msg,
type: 'error'
})
return Promise.reject(new Error(msg))
} else if (code !== 200) {
Notification.error({
title: msg
})
return Promise.reject('error')
} else {
return res.data
}
},
error => {
console.log('err' + error)
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
}
else if (message.includes("timeout")) {
message = "系统接口请求超时";
}
else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
基本上,每一个项目都会有一个下载的,所以还是得做下相关的处理,如文件请求,我们的Response格式会有很大的区别,一般都是Blob。外加一个弹窗,所以就这么简单。
// 通用下载方法
export function download(url, params, filename) {
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob'
}).then(async (data) => {
const isLogin = await blobValidate(data);
if (isLogin) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
Message.error(errMsg);
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close();
})
}
整体代码如下
import axios from 'axios' import { Notification, MessageBox, Message, Loading } from 'element-ui' import store from '@/store' import { getToken } from '@/utils/auth' import errorCode from '@/utils/errorCode' import { tansParams, blobValidate } from "@/utils/ruoyi"; import cache from '@/plugins/cache' import { saveAs } from 'file-saver' let downloadLoadingInstance; // 是否显示重新登录 export let isRelogin = { show: false }; axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' // request拦截器 service.interceptors.request.use(config => { // 是否需要设置 token const isToken = (config.headers || {}).isToken === false // 是否需要防止数据重复提交 const isRepeatSubmit = (config.headers || {}).repeatSubmit === false if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 } // get请求映射params参数 if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url; } if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
} } return config }, error => {
console.log(error)
Promise.reject(error) }) // 响应拦截器 service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if(res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer'){
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true;
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
isRelogin.show = false;
store.dispatch('LogOut').then(() => {
location.href = '/index';
})
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({
message: msg,
type: 'error'
})
return Promise.reject(new Error(msg))
} else if (code !== 200) {
Notification.error({
title: msg
})
return Promise.reject('error')
} else {
return res.data
} }, error => {
console.log('err' + error)
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
}
else if (message.includes("timeout")) {
message = "系统接口请求超时";
}
else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error) } ) // 通用下载方法 export function download(url, params, filename) { downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob' }).then(async (data) => {
const isLogin = await blobValidate(data);
if (isLogin) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
Message.error(errMsg);
}
downloadLoadingInstance.close(); }).catch((r) => {
console.error(r)
Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close(); }) } export default service
由于没啥还演示的,所以就不显示效果,不贴上效果图了。
主要介绍前端开发的博客,由衷期望各位大佬们扫码关注