一个通用的http方法应该有以下功能
(1)请求头携带通用数据,例如token、用户名称等
(2)对响应结果做统一的过滤处理:
①未登录进行登录重定向(有登录要求的系统才需要);
②二进制流数据处理(通常是文件下载)
③请求成功判断
②请求失败通用处理:弹窗提示错误信息(根据选用的组件做UI通用)
实现逻辑:
(1)通过请求拦截器和响应拦截器加上通用的逻辑
(2)如果使用了vue框架的还可以挂在在vue实例上,方便通过 this 访问
完整代码如下:
import axios from 'axios'
const myAxios = axios.create({
timeout: 30000, // 超时
withCredentials: true,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
/**
* 请求拦截器,用于添加统一的请求头
*/
myAxios.interceptors.request.use(function (config) {
// 添加token
if (sessionStorage.getItem('token') || localStorage.getItem('token')) { // 添加 token
config.headers['token'] = sessionStorage.token || localStorage.token
}
// 针对环境添加不同的请求头
if (process.env.NODE_ENV === 'development') {
config.headers['X-ENNAME'] = 'dev something'
} else if (process.env.NODE_ENV === 'production') {
config.headers['X-ENNAME'] = 'prd something'
config.headers['X-SOMETHING'] = 'prd something'
}
return config
}, function (err) {
showErrToast(err)
return Promise.reject(err)
})
/**
* 响应拦截器,对响应状态码做统一的处理,报错给出报错提示
*/
myAxios.interceptors.response.use(function (res) {
if (res.data.code === 400002) { // 没有登录,登出页面 // (状态码视后端服务而定:表示未登录)前端进行重定向登录,或者后端提供接口,前端直接打开接口,后端自己进行重定向
window.open('this is login page url or backend service loginout', '_self')
} else if (res.request.responseType === 'blob') { // 二进制流数据 直接返回
return res
} else if (res.status === 200 && res.data.code === 200) { // 请求成功直接抛出数据:成功状态码视后端服务而定
return res.data
} else { // 发生错误进行错误提示
showErrToast(res.data.message || res.statusText)
return Promise.reject(res.data)
}
}, function (err) {
showErrToast(err)
return Promise.reject(err)
})
/**
* 请求发生错误时的提示
* @param {*} e 错误信息
* 可以自定义,例如使用elementUI的错误提示弹窗 Message({ type: 'error', message: err })
*/
function showErrToast (err) {
alert(err)
}
export const $http = myAxios
/**
* 针对vue的封装:对axios的实例重新封装成一个plugin ,方便 Vue.use(xxxx),可以让$http挂在到vue上
* 通过 this.$http 访问
*/
export default {
install (Vue, Option) {
Object.defineProperty(Vue.prototype, '$http', { value: myAxios })
}
}
/**
* axios的config配置说明:https://www.axios-http.cn/docs/req_config
*/
// axios.request(config)
// axios.get(url[, config])
// axios.delete(url[, config])
// axios.head(url[, config])
// axios.options(url[, config])
// axios.post(url[, data[, config]])
// axios.put(url[, data[, config]])
// axios.patch(url[, data[, config]])
/**
* 使用说明
* 1. 如何挂在到vue上,可以通过 this.$http访问?
* // main.js
* import $http from '@/utils/http'
* Vue.use($http)
*
* 2.如何上传文件?
* 二进制 'Content-Type': 'application/json'不使用,需要更换请求头headers的content-type
* 例如:
* 'Content-Type': 'multipart/form-data'
* 'Content-Type': 'application/x-www-form-urlencoded'
* 可以通过config注入自行更换请求头的Content-Type
* this.$http.request({
headers: { 'Content-Type': 'multipart/form-data;charset=UTF-8' },
method: 'post',
url: '',
data: formData
})
*/
当离开A页面,进入B页面时,如何取消A页面进行中的请求,从而避免资源浪费。
axios提供了取消请求的方法 取消请求 | Axios 中文文档 | Axios 中文网
可以在请求拦截器中将每个请求的取消请求方法存储下来,再利用 router.beforeEach 取消请求
这里结合 vuex的store进行存储,当然也可以用别的方式。
import store from '@/store'
const CancelToken = axios.CancelToken // 取消请求
/**
* 请求拦截器,用于添加统一的请求头
*/
myAxios.interceptors.request.use(config => {
// 取消请求的方法存入store中
config.cancelToken = new CancelToken(cancel => {
store.commit('httpChannel/pushToken', { cancelToken: cancel })
})
return config
}, function (err) {
showErrToast(err)
return Promise.reject(err)
})
store如下:
export default {
namespaced: true,
state: {
cancelTokenArr: [] // 取消请求的数组
},
getters: {},
mutations: {
pushToken (state, payload) {
state.cancelTokenArr.push(payload.cancelToken)
},
clearToken (state) {
state.cancelTokenArr.forEach(item => {
if (Object.prototype.toString.call(item) === '[object Function]') {
item() // 取消请求
}
})
state.cancelTokenArr = []
}
},
actions: {}
}
接下来只需要在路由守卫监听每次页面跳转,跳转前取消pending中的请求
router.beforeEach((to, from, next) => {
store.commit('httpChannel/clearToken') // 取消请求
next()
})
上述通用的http封装,发生异常情况的时候会自动showErrToast错误提示。但是在这里,我不希望取消请求也被当成错误提示展示在页面里,所以,还需要进行改造一下:
/**
* 请求发生错误时的提示
* @param {*} e 错误信息
* 可以自定义,例如使用elementUI的错误提示弹窗 Message({ type: 'error', message: err })
*/
function showErrToast (err) {
if (err !== 'Cancel') {
Message({ type: 'error', message: err })
}
}