在vue项目中我们一般会使用axios来实现前后端的交互,下面是常用的axios封装,包括了请求头、状态码、请求超时时间、请求方式、请求拦截器和响应拦截器等的封装:
首先需要先安装axios,使用如下命令:
npm install axios
我们一般会在src目录下新建一个文件夹api,然后在此文件夹下创建一个request.js文件,对axios的封装代码就写在request.js文件中,目录结果见下图
主要内容拆解:
1.请求超时时间设置,可根据实际项目需求而定
const request = axios.create({
baseURL: '',
timeout: 60000, //改成60s
})
2.添加请求拦截器--(由于项目中有使用到上传和导入功能,参数与一般请求有区别,这里做了分类处理,如果不需要对参数做处理的,可以去掉type==='upload'的判断部分代码)
request.interceptors.request.use(
(config) => {
config.data = config.data || ''
const { data, type, basic } = config.data
if ( process.env.NODE_ENV === 'development' ) {
config.url = `${config.baseUrl || '/api'}${config.url}`
} else {
config.url = `${config.url}`
}
if (data && basic) return config //如果传参时有传 basic 则不需要再加一次basic
if (config.url !== '/login') { // 判断请求是否是登录接口
config.headers.token = localStorage.getItem('token') // 如果不是登录接口,就给请求头里面设置token
}
config.data = type === 'upload' ? data : formatParams(config.data)
return config // 返回这个配置对象,如果没有返回,这个请求就不会发送出去
},
(error) => {
return Promise.reject(error)
}
)
其中basic中包括token、ver(版本号)、time(时间戳)等公共的接口参数,一般我们会在src下的utils文件夹中创建一个basic.js的文件,代码如下:
import MD5 from 'js-md5'
import SHA512 from './secret/sha512' // sha512加密
import { getToken } from '@/utils/auth.js'
/**
* 生成32位随机正整数 0~2147483647 (2^32)
*/
export const getNonce = () => Math.floor(Math.random() * 2147483646 + 1)
/**
* 生成basic数据
* @param ver {string} 必填 -- 版本号
* @param time {number} 必填 -- 时间戳
* @param id {string} 必填 -- 0
* @param nonce {string} 必填 -- 32位随机数
* @param token {string} 非必填 -- 登陆成功后必填,从后台获取,在localStorage中存取
* @param sign {string} 非必填 -- 签名,从后台获取,在localStorage中存取
*/
export const getBasic = ({
ver = '1.0',
time = new Date().getTime(),
id = 1,
nonce = getNonce(),
token = getToken(),
sign
} = {}) => {
return { ver, time, id, nonce, token, sign }
}
/**
* 生成sign签名
* @param {Object} obj: 必须有pwd属性
* @param {array} argsArray 拼接项数组
*/
export const creatSignForDpikey = (obj, argsArray) => {
let str = ''
let arr = argsArray
if (!arr) {
arr = Object.keys(obj).filter(v => v !== 'pwd')
}
arr.sort()
for (let key of arr) {
str += obj[key]
str += '#'
}
str += MD5(obj['pwd'])
let Mstr = SHA512.encrypt(str)
return Mstr
}
2.添加响应拦截器--响应拦截器中包含对错误码的处理,此处我们一般会新建errorCode.js文件,将需要统一在页面上弹出提示的错误码写在此文件中(好处就是不需要在代码里面每个接口返回的错误码都写一次提示处理,只需要针对需要特别处理的个别错误码做处理例如提示后还有下一步操作的情况就需要特殊处理)
request.interceptors.response.use(
(res) => {
let langType = getLocale() || 'en-US'
if (res.status != 200) {
return false
}
let code
if (res.data.basic) {
code = res.data.basic.code // 获取后端返回的状态码
if (code === 200) { //接口状态码
return res.data
} else if (code === 20004 || code === 10002 || code === 7009 || code === 7088 || code === 7090 || code === 7004 ) { // Invalid token Token expires // Token达到最大数量后被强制退出
if (showLoginMsg) { // token过期导致请求失败,返回登录页后只弹出一个异常提示
Message.error(errorCode[langType][code])
logout()
showLoginMsg = false
setTimeout(() => {
showLoginMsg = true
}, 3000)
}
return Promise.reject(code)
}
else { //例如公共的 其他code 默认3秒 改为2秒
Message.error(errorCode[langType][code]) //改为这种写法后 即使没有翻译 codeMsg也不为false
if (codeMsg && (codeMsg !== `errorCode[langType][code]`)) {
if (showOtherMsg) {
Message({ message: codeMsg,duration: 2000,type: 'error' })
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
} else if (!specialCode.includes(code)) {
if (showOtherMsg) {
Message({ message: res.data.basic.msg,duration: 2000,type: 'error' })
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
}
return Promise.reject(res.data)
}
} else {
return res.data
}
},
(error) => {
// axios判断请求超时进行处理
let langType = getLocale() || 'en-US'
if (error.message.includes('timeout')) {
if (showOtherMsg) {
Message({
message:message: errorCode[langType]['12345'],
type: 'error',
duration: 2 * 1000,
})
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
} else if (error && error.response) {
// 处理失败的状态码 状态码404 500 503之类的
let statusCode = error.response.status
if (showOtherMsg) {
Message({ message: errorCode[langType][statusCode],duration: 2000,type: 'error' })
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
} else if (error.message == 'Network Error') {
if (showOtherMsg) {
Message({
message:errorCode[langType]['12344'],
type: 'error',
duration: 2 * 1000,
})
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
}
return Promise.reject(error)
}
)
request.js中的完整的代码如下:
/* eslint-disable no-undef */
import axios from 'axios'
import Vue from 'vue'
import router from '../router' // 获取路由
import { Message } from 'element-ui' // message提示信息组件
import { getBasic } from '@/utils/basic'
import { errorCode } from '@/utils/errorCode'
import { specialCode } from '@/utils/specialCode'
import { getLocale } from '@/lang'
import store from '@/store'
import { logout } from '@/utils/common'
// 生产地址
// axios.defaults.baseURL = serverBase; // 配置axios请求的地址
const request = axios.create({
baseURL: '',
timeout: 60000, //改成60s
})
// 格式化请求参数
const formatParams = (params) => {
const basic = getBasic()
return {
basic,
data: params
}
}
let showLoginMsg = true
let showOtherMsg = true
// 请求拦截器
request.interceptors.request.use(
(config) => {
config.data = config.data || ''
const { data, type, basic } = config.data
if ( process.env.NODE_ENV === 'development' ) {
config.url = `${config.baseUrl || '/api'}${config.url}`
} else {
config.url = `${config.url}`
}
if (data && basic) return config //如果传参时有传 basic 则不需要再加一次basic
if (config.url !== '/login') { // 判断请求是否是登录接口
config.headers.token = localStorage.getItem('token') // 如果不是登录接口,就给请求头里面设置token
}
config.data = type === 'upload' ? data : formatParams(config.data)
return config // 返回这个配置对象,如果没有返回,这个请求就不会发送出去
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器
request.interceptors.response.use(
(res) => {
let langType = getLocale() || 'en-US'
if (res.status != 200) {
return false
}
let code
if (res.data.basic) {
code = res.data.basic.code // 获取后端返回的状态码
if (code === 200) { //接口状态码
return res.data
} else if (code === 20004 || code === 10002 || code === 7009 || code === 7088 || code === 7090 || code === 7004 ) { // Invalid token Token expires // Token达到最大数量后被强制退出
if (showLoginMsg) { // token过期导致请求失败,返回登录页后只弹出一个异常提示
Message.error(errorCode[langType][code] )
logout()
showLoginMsg = false
setTimeout(() => {
showLoginMsg = true
}, 3000)
}
return Promise.reject(code)
}
else { //例如公共的 其他code 默认3秒 改为2秒
const codeMsg = errorCode[langType][code] //改为这种写法后 即使没有翻译 codeMsg也不为false
if (codeMsg && (codeMsg !== `errorCode[langType][code] `)) {
if (showOtherMsg) {
Message({ message: codeMsg,duration: 2000,type: 'error' })
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
} else if (!specialCode.includes(code)) {
if (showOtherMsg) {
Message({ message: res.data.basic.msg,duration: 2000,type: 'error' })
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
}
return Promise.reject(res.data)
}
} else {
return res.data
}
},
(error) => {
// axios判断请求超时进行处理
let langType = getLocale() || 'en-US'
if (error.message.includes('timeout')) {
if (showOtherMsg) {
Message({
message: errorCode[langType]['12345'],
type: 'error',
duration: 2 * 1000,
})
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
} else if (error && error.response) {
// 处理失败的状态码 状态码404 500 503之类的
let statusCode = error.response.status
if (showOtherMsg) {
Message({ message:errorCode[langType][statusCode],duration: 2000,type: 'error' })
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
} else if (error.message == 'Network Error') {
if (showOtherMsg) {
Message({
message: errorCode[langType]['12344'],
type: 'error',
duration: 2 * 1000,
})
showOtherMsg = false
setTimeout(() => {
showOtherMsg = true
}, 2000)
}
}
return Promise.reject(error)
}
)
export default request
在项目中使用示例: