axios封装(附带token静默刷新)

大家使用axios最多的情况应该是和vue配套开发,所以在实际开发的时候我们是可以做一套axios封装的。方便我们管理http请求部分的代码。

参考:axios如何利用promise无痛刷新tokenvue前端刷新token

写在前面

因为公司有保密软件,所以代码是不允许复制的,然后博客中的代码时我对照手敲的,可能存在一些变量啥的或者什么东西落下的(因为只是针对博客出代码,自己项目的代码不可能全部对照敲出来的,只能说我的代码是实际运用在项目中的)希望大家可以指出来,我会实时修改的。

文件目录
├── src
	├── request
		├── urls.js
		├── http.js
		├── api.js

urls.js

urls.js:这个是我用来管理所有的接口地址的,大家可以根据自己的需求来决定需不需要

export default {
    base: {
        /**登录相关 */
        signIn: '/ums/web/login',//登录
        signOut: '/ums/web/logout',//退出
        refresh: '/ums/web/token/refresh',//刷新token
		..........
	}
}
http.js

http.js:这个就是最主要的封装文件

/**axios封装
 * 请求拦截、相应拦截、错误统一处理
 */
import axios from 'axios';
import router from '@/router'

// 创建axios实例
var instance = axios.create({
    timeout: 1000 * 12
});
// 设置post请求头
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

/** 
 * 跳转登录页
 * 携带当前页面路由,以期在登录页面完成登录后返回当前页面
 */
const toLogin = () =>{
    // 清除用户登录信息                 
    window.localStorage.clear();
    router.replace({
        path: '/login'
    });
}
/** 
 * 请求失败后的错误统一处理 
 * @param {Number} status 请求失败的状态码
 */

const errorHandle = (status, other) = >{
    switch (status) {
        // 401: 未登录状态,跳转登录页
    case 401:
        toLogin();
        break;
    case 403:
        setTimeout(() =>{
            toLogin();
        },
        1000);
        break;
        ase 404 : break;
    default:
        console.log(other);
    }
}
//请求拦截器
instance.interceptors.request.use(req =>{
	//这里可以根据项目需求在请求前进行拦截处理并返回req
	//例子(为所有请求加上token)
	let token = localStorage.getItem('token') || '';
    req.headers.common['authorization'] = token
	return req;

})
// 响应拦截器
instance.interceptors.response.use(
//http请求200
res =>{
    if (res.status === 200) {
    	//这里是我和后台定的状态码
        if (res.data.code == 3000 || res.data.code == 2000) {
            return Promise.resolve(res)
        } else {
            //退出成功,未认证
            if (res.data.code == 2006 || res.data.code == 2001) {
                toLogin();
            } else {//未知错误
                return Promise.reject(res)
            }
        }
    } else {
        return Promise.reject(res)
    }
},
// 请求失败
error =>{
    const {
        response
    } = error;
    if (response) {
        // 请求已发出,但是不在2xx的范围 
        errorHandle(response.status, response.data.message);
        return Promise.reject(response);
    } else {
        // 处理断网的情况
        return Promise.reject(` {
            status: -1,
            message: '断网了',
            data: null
        }`);
    }
}); 
export default instance;
api.js

api.js:这里是我们调用封装后的axios然后暴露方法出去的地方


// 导入http中创建的axios实例
import axios from './http';
// urls接口地址
import urls from './urls'
const base = urls.base;

//登录
const login = {
    signIn: (params) => { return axios.post(`${base.signIn}`, params) },
    signOut: (params) => { return axios.get(`${base.signOut}`, params) },
    refreshToken: (params) => { return axios.get(`${base.refresh}`, { params: params }) }
}
export default {
	login,
}
使用

main.js

import api from '@/request/api' // 导入api接口
Vue.prototype.$api = api; // 将api挂载到vue的原型上

login.vue

methods:{
	login(){
		const _this = this;
		this.$api.login
            .signIn({
            	username:this.username,
            	password:this.password
            })
            .then(res=>{
            	//存储token和失效时间
            	localStorage.setItem("isLogin", true); //是否登录状态
            	localStorage.setItem("token", res.data.data.authorization); //token
            	localStorage.setItem(
                    "expired_time",
                    res.data.data.expired_time
                ); //过期时间
                _this.$router.push({ path: "/home" });
			})
	}
}
额外:token静默刷新

因为token存在失效时间(我们定义的token是登录的时候就定死了失效时间,所以过了失效时间后是需要刷新重新获取新的token和新的token失效时间)。所以就存在可能用户断断续续操作了一段时间再点击就提示token失效了,这肯定是不是很友好,所以我就需要token静默刷新,让用户体验更好一点。

我也是网上找了很多资料才弄好的并且理解了这种做法(请原谅我的渣),前面我也贴出了我的参考博客,虽然有照抄嫌疑,但是我刚开始只是照搬发现不对,然后后面慢慢实验并且理解他的写法之后才成功的。

这里我是将检测是否token失效放在了请求拦截器中,我觉得这里更好控制,也有博主放在响应拦截器中,我上面也贴了博客地址。

http.js (token刷新)

import urls from './urls'
const base = urls.base;
const base = urls.base;

// 判断token是否失效
const checkToken = () =>{
    let expired_time = localStorage.getItem('expired_time') || '';
    let key = false;
    if (expired_time) {
        let nowTime = new Date();
        let eprTime = new Date(expired_time);
        key = nowTime.getTime() > eprTime.getTime();
    }
    return key;
}

let isRefreshToken = false; //防止同一时间段多次刷新
/*存储请求的数组*/
let refreshSubscribers = []

/*将所有的请求都push到数组中,其实数组是[function(token){}, function(token){},...]*/
function subscribeTokenRefresh(cb) {
    refreshSubscribers.push(cb);
}

/*数组中的请求得到新的token之后自执行,用新的token去请求数据*/
function onRrefreshed(token) {
    refreshSubscribers.map(cb =>cb(token));
}

// 刷新token
function refreshToken() {
    return new Promise((resolve, reject) =>{
        axios.get(base.refresh, {
            params: {
                token: localStorage.getItem('token')
            },
            headers: {
                'authorization': localStorage.getItem('token')
            } //设置header信息
        }).then(res =>{
            if (res.data.code == 3000) {
                // 重新存储token和失效时间
                localStorage.setItem('token', res.data.data.authorization);
                localStorage.setItem("expired_time", res.data.data.expired_time);
                resolve();
            } else {
                if (res.data.code == 2001) { //未认证
                    toLogin();
                }
                reject()
            }
        }).
        catch(error =>{
            reject()
        })
    })
}

// 请求拦截器
instance.interceptors.request.use((req) =>{
    // 登录状态
    let isLogin = localStorage.getItem('isLogin');
    let token = localStorage.getItem('token') || '';
    req.headers.common['authorization'] = token
    let isExpired = checkToken();
    if (isLogin && isExpired) { //已登录的话判断是否token是否过期
        if (!isRefreshToken) { //是否正在刷新
            isRefreshToken = true;
            refreshToken().then(res =>{
                // 重新设置token
                let token = localStorage.getItem('token') || '';

                onRrefreshed(token) refreshSubscribers = []
            }).
            catch(error =>{ //请求失败的话重新登陆
                // console.log(error)
                refreshSubscribers = [] toLogin();
            }).
            finally(() =>{
                isRefreshToken = false;
            });
        }
        /*将请求挂起 callback回调函数执行 等待token刷新完成return promise对象*/
        let retry = new Promise((resolve, reject) =>{
            subscribeTokenRefresh((token) =>{
                req.headers.common['authorization'] = token;
                resolve(req)
            })
        });

        return retry;

    } else {
        // return req;
        return Promise.resolve(req);
    }
}

这里主要是运用了promisecallback。使得请求能够在进来后使用promise让请求挂起(pending状态)然后刷新token后通过callback重新发起请求并且将promise状态变为resolve

因为请求拦截器返回出来的是一个promise对象,所以return Promise resolve(req)return req是一样的,这样就保证了下面retry函数返回出去后可以保证后续执行。

这里也保证了同一时间段(token失效)不管进来多少请求都会被挂起,只有等到token重新刷新完成后才会逐个重启请求。

也不知道我的理解有没有问题。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
axios封装中,可以通过在请求拦截器中添加token来实现。在引用\[1\]中的代码中,可以看到在请求拦截器中设置了请求头的Authorization字段为Bearer加上token的值。这样在每次发送请求时,都会带上token作为身份验证信息。同时,在引用\[2\]中的代码中,可以看到通过Cookie来存储和获取token的值。在setToken函数中,可以将传入的token值存储在Cookie中,而在getToken函数中,可以获取存储在Cookie中的token值。这样就可以在请求拦截器中获取到token的值并设置到请求头中。这样就完成了axios封装,实现了token的使用。 #### 引用[.reference_title] - *1* *3* [【Vue3】Axios简易二次封装+token+跨域问题+接口同步调用使用。](https://blog.csdn.net/Lin_Yu_/article/details/127783783)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [封装token](https://blog.csdn.net/weixin_54308174/article/details/126632209)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值