1、简单的 axios 请求和响应拦截配置
2、取消重复请求
import axios from 'axios'
import debounce from 'lodash/debounce'
import {
Message
} from 'element-ui'
class Ajax {
constructor() {
// 初始化值
this.init()
// 初始化token
this.getToken()
// 初始化axios
this.initAxios()
// 打开请求拦截
this.startReqInterceptors()
// 打开响应拦截
this.startRespInsterceptors()
// 返回 axios 实例对象
return this.ajax
}
/**
* @method 初始化值
*/
init() {
this.overTimeValue = 30
this.baseURL = '/new-trade-bank'
this.pending = []
this.CancelToken = axios.CancelToken
}
/**
* 获取token
*/
getToken() {
this.token = localStorage.getItem('token')
this._token = localStorage.getItem('ac_token')
}
/**
* @method 初始化axios实例
*/
initAxios() {
this.ajax = axios.create({
timeout: this.overTimeValue * 1000
})
}
/**
* @method 通过config获取到参数
* @returns 对应参数的字符串
*/
getParamFormConfig(config) {
const paramsMethodMap = {
get: 'params',
// delete 请求 data和params都可以
delete: 'data',
post: 'data',
put: 'data',
patch: 'data'
}
// 参数
const param = config[paramsMethodMap[config.method]]
// 参数进行格式化
const format = {
'[object Undefined]': undefined,
'[object Array]': JSON.stringify(param),
'[object Object]': JSON.stringify(param),
'[object Number]': param,
'[object String]': param
}
// 返回字符串格式
return format[Object.prototype.toString.call(param)]
}
/**
* @method 打开请求拦截
*/
startReqInterceptors() {
const {
token,
_token
} = this
this.ajax.interceptors.request.use(config => {
if (config.url) {
// 包含/special开头的api做特殊处理,不用baseURL
config.url = /^(\/special)/.test(config.url) ? config.url : this.baseURL + config.url
// 发送ajax前,执行取消操作,便利数组 看是否重复发送
this.removePending(config)
// 设置token和请求头authorization
if (token) {
config.headers.token = token
} else if (_token) {
config.headers.Authorization = `bearer ${_token}`
}
// 将新请求添加进队列
this.addPending(config)
}
// 做全局加载中
return config
})
}
/**
* @method 打开响应拦截
*/
startRespInsterceptors() {
this.ajax.interceptors.response.use(resp => {
// 当前请求已经响应结束,从pending队列中移除
this.removePending(resp.config)
let { data } = resp
if (resp.status === 200) {
const {
filename
} = resp.headers
if (!filename && data.type && data.type !== 'success') {
this.alertMsg(data)
}
if (data && filename) {
// 对文件下载特殊处理
const str = data
data = {
fileStream: str,
filename
}
}
return data || resp
}
}, err => {
if (!err.response) {
/* 重复发送请求,请求被取消了 */
const {
message
} = err
// 超时
if (/^(timeout of)/.test(message)) {
// 移除超时请求
this.removeOverTimeRequest()
this.alertMsg({
type: 'error',
text: '接口请求超时'
})
} else {
console.warn('Warning: 重复发送了请求:' + err.message)
}
} else if (err.response.status === 401) {
// 移除token
const {
token
} = this
window.localStorage.clear()
// 没有权限跳转到权限认证页面
window.location.href = token ? `/new-trade-bank/authorize?token=${token}` : '/new-trade-bank/authorize'
} else if (err.response.status === 403) {
this.alertMsg(err.response.data)
} else if (err.response.status === 404) {
console.warn('%c404-后台接口未找到', 'font-weight: bold;color: red;font-size: 16px;')
} else if (err.response.status === 500) {
this.alertMsg({
type: 'error',
text: '500-连接服务器出错'
})
}
/**
* 在这里对状态吗不是200的进行统一错误捕获
* 注意: 这里代码执行完了 还是会走Promise.resolve
* 也就是还是会走请求的.then函数
* 返回可以按照自己需要进行返回,可以减少请求出错时的控制台报错
*/
return Promise.reject(err)
})
}
/**
* @method 请求成功,移除队列中的请求
*/
removePending(target) {
// target的param需要经过格式化成相同字符串才能比较
const targetParam = this.getParamFormConfig(target)
for (const [index, p] of Object.entries(this.pending)) {
// url,method,param需要完全一致才能确定是重复请求
if (p.url === target.url && p.method === target.method && targetParam === p.param) {
// 当当前请求在数组中存在执行函数体,取消队列中的请求并删除,执行cancel函数会传递一个
// cancel对象给promise.reject, cancel里面的参数会传递到response中的err对象的message中
p.cancel(`${p.method} ${p.url}`)
// 从队列中删除
this.pending.splice(index, 1)
}
}
}
/**
* @method 将请求添加进队列中
*/
addPending(config) {
config.cancelToken = new axios.CancelToken(c => {
this.pending.push({
url: config.url,
method: config.method,
cancel: c,
param: this.getParamFormConfig(config),
createTime: Date.now()
})
})
}
/**
* @method 通过element的message组件进行统一弹窗告警
*/
alertMsg(data) {
// 关闭现在有的告警信息
Message.closeAll()
Message({
showClose: true,
message: data.text,
type: data.type
})
}
}
/**
* @method 移除pending队列中的超时请求
* @description 通过防抖避免频繁调取
*/
Ajax.prototype.removeOverTimeRequest = debounce(function () {
const nowDate = Date.now()
for (const p in this.pending) {
const {
createTime
} = this.pending[p]
const time = nowDate - createTime
if (time >= (this.overTimeValue * 1000)) {
this.pending.splice(p, 1)
}
}
}, 100)
const ajax = new Ajax()
export default ajax