vue框架搭建04-axios封装
/**
* axios封装
* 缺点:axios内也只是封装了get 和post 类型没有完全写完。
* 对 config 对象没有进行扩展,如果出现特殊http请求类型
* 优点:对重复请求接口进行了拦截,
* 页面跳转时取消了全部http请求
*/
import {Message} from "element-ui"
// axios封装
// 1.引入axios
import axios from 'axios'
import {addPending, removePending} from "./cancleRequest.js"
// 2. 配置信息
const config = {
// baseURL:'', // 每次请求的协议、IP地址。 设置该配置后,每次请求路径都可以使用相对路径,例如"/admin/login"
// timeout: 0, // 请求超时时间
timeout: 60 * 1000 * 1, // 请求超时时间
withCredentials: true,// 每次请求携带cookie
headers: {
'Content-Type': 'application/json; charset=utf-8',
"RemoveToken": false, // 自定义参数 true 不携带 token 参数请求 false 携带
"NoCheck": false // 自定义参数 true 不做响应拦截 false 拦截
},
}
// 3.创建实例
const instance = axios.create(config)
// 1. 请求拦截
instance.interceptors.request.use(
// 请求之前做些什么
config => {
let data = config.data || config.params || {}
const url = [config.method, config.url, JSON.stringify(config.params), JSON.stringify(config.data)].join('&')
removePending(url) // 在请求开始前,对之前的请求做检查取消操作
addPending(config) // 将当前请求添加到 pending 中
if (config.headers.RemoveToken) {
delete config.headers.Authorization
} else {
const token = sessionStorage.getItem('token')
config.headers.Authorization = "Bearer " + token
}
return config
},
// 处理错误
error => {
return Promise.reject(error)
}
)
// 2. 响应拦截
instance.interceptors.response.use(
// 对于成功响应的处理
response => {
const url = [
response.config.method,
response.config.url,
response.config.params,
response.config.data
].join('&')
removePending(url) // 在请求结束后,移除本次请求
//headers 如果NoCheck为true,将会跳过拦截
if (response.config.headers.NoCheck) {
return response.data
} else {
const code = response.data.code
const message = response.data.msg
switch (code) {
case 200:
break;
case 500:
Message({
type: 'error',
dangerouslyUseHTMLString: true,
message: message || '接口报错'
})
break;
case 401:
Message({
type: 'error',
message: '用户未登录或者已超时!'
})
if (!window.location.href.includes('login')) {
setTimeout(() => {
window.location.href = '/'
}, 1000)
}
break
default:
Message({
type: 'error',
dangerouslyUseHTMLString: true,
message: message || "未知错误,请重试"
})
break;
}
return response.data
}
},
// 处理错误响应
error => {
if (JSON.stringify(error).indexOf('timeout of') !== -1) {
Message.error('请求超时,请求已被取消')
console.error('请求超时,请求已被取消')
}
// 判读异常是否是由取消请求导致的,
else if (axios.isCancel(error)) {
Message.error('请求太频繁')
console.error('repeated request: ' + error.message)
} else {
switch (error.response.status) {
case 400:
Message.error('错误请求')
console.error('错误请求')
break
case 401:
Message.error('未授权,请重新登录')
console.error('未授权,请重新登录')
break
case 403:
Message.error('拒绝访问')
console.error('拒绝访问')
break
case 404:
Message.error('请求错误,未找到该资源')
console.error('请求错误,未找到该资源')
break
case 405:
Message.error('请求方法未允许')
console.error('请求方法未允许')
break
case 408:
Message.error('请求超时')
console.error('请求超时')
break
case 500:
Message.error('服务器端出错')
console.error('服务器端出错')
break
case 501:
Message.error('网络未实现')
console.error('网络未实现')
break
case 502:
Message.error('网络错误')
console.error('网络错误')
break
case 503:
Message.error('服务不可用')
console.error('服务不可用')
break
case 504:
Message.error('网络超时')
console.error('网络超时')
break
case 505:
Message.error('http版本不支持该请求')
console.error('http版本不支持该请求')
break
default:
Message.error(`连接错误${error.response.status}`)
error.Message = `连接错误${error.response.status}`
}
}
// return Promise.reject(error)
return Promise.resolve({})
}
)
// 二次封装
const request = {
get(url, param, headers = null) {
return new Promise((resolve, reject) => {
let config = {method: "get", url, params: param}
if (headers) {
Object.assign(config, {headers})
}
instance(config).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
},
post(url, param, headers = null) {
return new Promise((resolve, reject) => {
let config = {method: "post", url, data: param}
if (headers) {
Object.assign(config, {headers})
}
instance(config).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
}
export default request
cancleRequest.js
import axios from 'axios' // 声明一个 Map 用于存储每个请求的标识 和 取消函数 const pending = new Map() /** * 添加请求 * @param {Object} config */ const addPending = (config) => { const url = [ config.method, config.url, JSON.stringify(config.params), JSON.stringify(config.data) ].join('&') config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => { if (!pending.has(url)) { // 如果 pending 中不存在当前请求,则添加进去 pending.set(url, cancel) } }) } /** * 移除请求 * @param {Object} config */ const removePending = (url) => { if (pending.has(url)) { // 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除 const cancel = pending.get(url) cancel(url) pending.delete(url) } } /** * 清空 pending 中的请求(在路由跳转时调用) */ const clearPending = () => { for (const [url, cancel] of pending) { cancel(url) } pending.clear() } export { addPending, removePending, clearPending }
在index.js的router.beforeEach
下进行页面跳转时的拦截
总结:
缺点:axios内也只是封装了get 和post 类型没有完全写完。
对 config 对象没有进行扩展,如果出现特殊http请求类型
优点:对重复请求接口进行了拦截,
页面跳转时取消了全部http请求
第二次补充
我一开始就是网上搜索然后自己做的。是按这种方式。
但是后来测试的时候发现
在 请求拦截里返回的是 config对象,请求参数也是一个对象。
但是在响应拦截请求里 返回的response.config的data是一个字符串。
所以用
const url = [
config.method,
config.url,
JSON.stringify(config.params),
JSON.stringify(config.data)
].join('&')
这种方法在removePending中回导致错误。
比如请求参数是{a:'b'}在请求拦截中JSON.stringify变成“{a:'b'}”,
但是在响应拦截中的data是已经是“{a:'b'}”了,再用JSON.stringify方法会变成”“{a:'b'}”“,导致用has()判断时不一样。
永远无法从pendding消除.
最后测试了一个请求发生时候的逻辑,
添加之前先进移除方法。再进添加方法,
请求完成再进移除方法。这才是对的
第三次修改
绿色背景色为第三次改变。
RemoveToken 为了取消 token 参数
NoCheck 为了取消 拦截 参数