字数
1431 字
阅读时间
7 分钟
概述
背景:面对大型项目,可以对 api 进行统一管理,有利于后期维护
二次封装 axios 的几个核心点:
- 请求前 token 验证
- 响应前处理不同状态码,响应出错是统一的 msg 错误描述
- 封装 get,post,patch 等 http 请求
- 对接配置文件的
baseApi和mockApi
公共依赖配置引入
js
/**
* 二次封装网络请求模块
*/
import axios from 'axios'
import config from '@/config/index'
import { ElMessage } from 'element-plus'
const TOKEN_INVALID = 'Token认证失败,请重新登录'
const NETWORK_ERROR = '网络请求失败,请稍后再试'
const ajax = axios.create({
baseURL: config.baseApi,
timeout: 8 * 1000
})请求响应拦截
js
// 配置请求拦截
ajax.interceptors.request.use(
(req) => {
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
// const token = this.$store.state.Authorization
// token&&(req.headers.Authorization = token)
const { headers } = req
if (!headers.authorization) {
headers['authorization'] = 'Bearer JIE'
}
return req
},
(error) => {
return Promise.error(error)
}
)
// 配置响应拦截
ajax.interceptors.respond.use(
(res) => {
const { code, data, msg } = res
if (code === 200) {
return data
} else if (code === 50001) {
ElMessage.error(TOKEN_INVALID)
return Promise.reject(TOKEN_INVALID)
} else {
ElMessage.error(msg || NETWORK_ERROR)
return Promise.reject(msg || NETWORK_ERROR)
}
},
// 服务器返回不是200的情况
(error) => {
if(error.response.status){
switch (error.response.status){
//401 未登录,未登录则跳转登录页面,并携带当前页面的路径
//在登录成功后返回当前页面,这一步需要在登录页操作
case 401:
this.$router.replace({
path: '/login',
query: {
redirect: this.$router.currentRoute.fullpath
}
})
break
//403 token过期,登录过期对用户进行提示
//清除本地token和清空vuex中的token对象,跳转到登录页面
case 403:
this.$toast({
message: '登录过期,请重新登录',
duration: 1000,
forbidClick: true
})
//清除token
localStorage.removeItem('Authorization')
this.$store.commit('changeLogin',null)
//跳转登录页面,并将要浏览的页面的fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
this.$router.replace({
path: '/Login',
query: {
redirect: this.$router.currentRoute.fullPath
}
})
},1000)
break
//404请求不存在
case 404:
this.$toast({
message: '网络请求不存在',
duration: 1500,
forbidClick: true
})
break
//其他错误,直接抛出错误提示
default:
this.$toast({
message: error.response.data.message,
duration: 1500,
forbidClick: true
})
}
return Promise.reject(error.response)
}
}
)对接 config 配置文件
js
/**
* axios请求核心函数
*/
const request = (options) => {
// 如果不传入请求方法,默认为 get 请求
options.method = options.method || 'get'
// 若为 get 请求,则将 data 转换为 params
if (options.method.toLowerCase() === 'get') options.params = options.data
if (config.dev === 'prod') {
// 如果是生产环境,则使用baseApi
ajax.defaults.baseURL = config.baseApi
} else if (options['mock']) {
// 如果这个请求传入了 mock=true 选项,则使用 MOCK api
ajax.defaults.baseURL = config.mockApi
} else {
// config 文件传入 MOCK 参数? 传入了那么我就用mockApi
ajax.defaults.baseURL = config.mock ? config.mockApi : config.baseApi
}
}封装 http 请求
js
// 封装 request.get...等方法
['get', 'post', 'put', 'delete', 'patch'].forEach(item => {
request[item] = (url, data, options) => {
return request({
url,
data,
...options,
method: item
})
}
})统一写法
js
/**
* 二次封装网络请求模块
*/
import axios from 'axios'
import config from '@/config/index'
import { ElMessage } from 'element-plus'
const TOKEN_INVALID = 'Token认证失败,请重新登录'
const NETWORK_ERROR = '网络请求失败,请稍后再试'
const ajax = axios.create({
baseURL: config.baseApi,
timeout: 8 * 1000
})
// 配置请求拦截
ajax.interceptors.request.use(
(req) => {
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
// const token = this.$store.state.Authorization
// token&&(req.headers.Authorization = token)
const { headers } = req
if (!headers.authorization) {
headers['authorization'] = 'Bearer JIE'
}
return req
},
(error) => {
return Promise.error(error)
}
)
// 配置响应拦截
ajax.interceptors.respond.use(
(res) => {
const { code, data, msg } = res
if (code === 200) {
return data
} else if (code === 50001) {
ElMessage.error(TOKEN_INVALID)
return Promise.reject(TOKEN_INVALID)
} else {
ElMessage.error(msg || NETWORK_ERROR)
return Promise.reject(msg || NETWORK_ERROR)
}
},
// 服务器返回不是200的情况
(error) => {
if(error.response.status){
switch (error.response.status){
//401 未登录,未登录则跳转登录页面,并携带当前页面的路径
//在登录成功后返回当前页面,这一步需要在登录页操作
case 401:
this.$router.replace({
path: '/login',
query: {
redirect: this.$router.currentRoute.fullpath
}
})
break
//403 token过期,登录过期对用户进行提示
//清除本地token和清空vuex中的token对象,跳转到登录页面
case 403:
this.$toast({
message: '登录过期,请重新登录',
duration: 1000,
forbidClick: true
})
//清除token
localStorage.removeItem('Authorization')
this.$store.commit('changeLogin',null)
//跳转登录页面,并将要浏览的页面的fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
this.$router.replace({
path: '/Login',
query: {
redirect: this.$router.currentRoute.fullPath
}
})
},1000)
break
//404请求不存在
case 404:
this.$toast({
message: '网络请求不存在',
duration: 1500,
forbidClick: true
})
break
//其他错误,直接抛出错误提示
default:
this.$toast({
message: error.response.data.message,
duration: 1500,
forbidClick: true
})
}
return Promise.reject(error.response)
}
}
)
/**
* axios请求核心函数
*/
const request = (options) => {
// 如果不传入请求方法,默认为 get 请求
options.method = options.method || 'get'
// 若为 get 请求,则将 data 转换为 params
if (options.method.toLowerCase() === 'get') options.params = options.data
if (config.dev === 'prod') {
// 如果是生产环境,则使用baseApi
ajax.defaults.baseURL = config.baseApi
} else if (options['mock']) {
// 如果这个请求传入了 mock=true 选项,则使用 MOCK api
ajax.defaults.baseURL = config.mockApi
} else {
// config 文件传入 MOCK 参数? 传入了那么我就用mockApi
ajax.defaults.baseURL = config.mock ? config.mockApi : config.baseApi
}
}
// 封装request.get...等方法
['get', 'post', 'put', 'delete', 'patch'].forEach(item => {
request[item] = (url, data, options) => {
return request({
url,
data,
...options,
method: item
})
}
})
export default request