参考axios官方文档
// 响应拦截器
// Add a response interceptor
request.interceptors.response.use(// 在2xx范围内的任何状态代码都会触发此函数,这里主要用于处理响应数据response => {return response},// 任何超出2xx范围的状态码都会触发此函数,这里主要用于处理响应错误error => {const { status } = error.responseif (status === 401) { // 未授权} else if (status === 403) { // 没有权限} else if (status === 404) { // 资源不存在Toast.fail({message: '请求资源不存在',forbidClick: true})} else if (status >= 500) { // 服务端异常Toast.fail({message: '服务端异常,请稍后重试',forbidClick: true})}// 将未处理的异常往外抛return Promise.reject(error)})
处理 token 过期
token过期后,如果发现存在user和token则发送 刷新token的请求,请求头中携带上refresh_token的值,
请求成功后,把获取到的最新的token设置给vuex容器中的user。
因为每次请求时,请求拦截器会从vuex容器中拿token。
这样即使用户token过期了,也会自动去发送刷新token的请求来获取最新的token,而不需要用户手动去登录!
/*
* 请求模块
* */
import axios from 'axios'
// 在非组件模块中获取store 必须采用这种方式
import store from '../store/index.js'
import JSONbig from 'json-bigint'
import { Toast } from 'vant'
import router from '../router'const request = axios.create({baseURL: '/api', // 基础路径transformResponse: [function (data) {// Do whatever you want to transform the data// console.log(data)// 后端返回的数据可能不是 JSON 格式字符串// 如果不是的话,那么 JSONbig.parse 调用就会报错// 所以我们使用 try-catch 来捕获异常,处理异常的发生try {// 如果转换成功,则直接把结果返回return JSONbig.parse(data)} catch (err) {console.log('转换失败', err)// 如果转换失败了,则进入这里// 我们在这里把数据原封不动的直接返回给请求使用return data}// axios 默认在内部使用 JSON.parse 来转换处理原始数据// return JSON.parse(data)}]
})const refreshTokenReq = axios.create({baseURL: '/api' // 基础路径
})
// 请求拦截器
request.interceptors.request.use(function (config) {const { user } = store.state// 如果用户已登录,统一给接口设置tokenif (user) {config.headers.Authorization = `Bearer ${user.token}`}// 处理完之后一定要把config 返回,否则请求发不出去return config
}, function (err) {return Promise.reject(err)
})// 响应拦截器
request.interceptors.response.use(function (response) {// 响应成功进入这里return response
}, async function (error) {// 响应失败进入这里const status = error.response.statusif (status === 400) {// 客户端请求参数错误Toast.fail('客户端请求参数错误')} else if (status === 401) {// token无效// 如果没有 user 或者 user.token, 直接去登录const { user } = store.stateif (!user || !user.token) {// 直接跳转到登录页面return redirectLogin()}// 使用refresh_token,则请求获取新的tokentry {const { data } = await refreshTokenReq({method: 'PUT',url: '/app/v1_0/authorizations',headers: {Authorization: `Bearer ${user.refresh_token}`}})// 拿到新的token之后把它更新到容器中user.token = data.data.tokenstore.commit('setUser', user)// 把失败的请求重新发送出去// error.config 是本次请求的相关配置项// 注意: 这里使用request发请求,它会走自己的请求拦截器,它的请求// 拦截器中通过store容器访问token数据return request(error.config)// console.log(data)} catch (err) {// 刷新token都失败了,直接跳转到登录页redirectLogin()}} else if (status === 403) {// 没有权限操作Toast.fail('没有权限操作')} else if (status >= 500) {// 服务端异常Toast.fail('服务端异常,请稍后重试!')}// console.dir(error)return Promise.reject(error)
})function redirectLogin () {router.replace('/login')
}// 导出
export default request
登录成功跳转回原来页面
首先在响应拦截器中:
注意:
router.currentRoute 和 我们在组件中获取的this.$route是一个东西
// 响应拦截器
request.interceptors.response.use(// 响应成功进入第1个函数// 该函数的参数是响应对象function (response) {// Any status code that lie within the range of 2xx cause this function to trigger// Do something with response datareturn response},// 响应失败进入第2个函数,该函数的参数是错误对象async function (error) {// Any status codes that falls outside the range of 2xx cause this function to trigger// Do something with response error// 如果响应码是 401 ,则请求获取新的 token// 响应拦截器中的 error 就是那个响应的错误对象console.dir(error)if (error.response && error.response.status === 401) {// 校验是否有 refresh_tokenconst user = store.state.userif (!user || !user.refresh_token) {// router.push('/login')
+ redirectLogin()// 代码不要往后执行了return}// 如果有refresh_token,则请求获取新的 tokentry {const res = await axios({method: 'PUT',url: 'http://ttapi.research.itcast.cn/app/v1_0/authorizations',headers: {Authorization: `Bearer ${user.refresh_token}`}})// 如果获取成功,则把新的 token 更新到容器中console.log('刷新 token 成功', res)store.commit('setUser', {token: res.data.data.token, // 最新获取的可用 tokenrefresh_token: user.refresh_token // 还是原来的 refresh_token})// 把之前失败的用户请求继续发出去// config 是一个对象,其中包含本次失败请求相关的那些配置信息,例如 url、method 都有// return 把 request 的请求结果继续返回给发请求的具体位置return request(error.config)} catch (err) {// 如果获取失败,直接跳转 登录页console.log('请求刷线 token 失败', err)// router.push('/login')
+ redirectLogin()}}return Promise.reject(error)}
)+ function redirectLogin () {
+ // router.currentRoute 当前路由对象,和你在组件中访问的 this.$route 是同一个东西// query 参数的数据格式就是:?key=value&key=value
+ router.push('/login?redirect=' + router.currentRoute.fullPath)
+ }
然后在登录成功以后:
const redirect = this.$route.query.redirect || "/";
this.$router.push(redirect);