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 为了取消 拦截 参数

 

 

 

 

 


 

posted @ 2021-04-07 13:46  辛夷不改年年色  阅读(105)  评论(0编辑  收藏  举报