当前是vue3+ts版本的封装

vue3+js版本请求封装可参考 https://www.cnblogs.com/lovejielive/p/14343619.html

token无感刷新,可自行删除 requset.ts 中 ts 相关数据恢复vue2版本

先在目录下创建 utils 和 common 这2个文件夹

utils 是存放工具类的,common 用来放置常用方法的

之后在utils 中创建 requset.ts 用来放置 uni.request 的请求方法,无感刷新。

1.common 文件创建 operate.ts + api.ts

主要用来放置 ,请求接口地址,一些全局请求数据,判断是否登录。

配置全局消息提示框,模拟对话框方法,方便调用

operate.ts 代码如下:

import store from '@/store/index'

export default {
//接口
api: function () {
let url = ''
// #ifdef MP-WEIXIN || MP-ALIPAY
let version = uni.getAccountInfoSync().miniProgram.envVersion;
switch (version) {
case "develop": //开发预览版
url = ''
break;
case 'trial': //体验版
url = ''
break;
case 'release': //正式版
url = ''
break;
default: //未知,默认调用正式版
url = ''
break;
}
// #endif
// #ifdef H5 || APP-PLUS
if (process.env.NODE_ENV === 'development') {
// console.log('开发环境')
url = ''
} else {
// console.log('生产环境')
url = ''
}
// #endif return url
}, //共同请求参数
commonBeg: function () {
return {
Authorization: this.isToken(),
}
}, //是否已注册(登录状态)
isLogin: function () {
return store.state.user.hasLodin;
}, //获取用户token
isToken: function () {
if (store.state.user.accessToken != '') {
return 'Bearer ' + store.state.user.accessToken;
}
return '';
}, //消息提示框
toast: function (options : any) {
uni.showToast({
title: options.title,
duration: options.duration || 2000,
icon: options.icon || "none"
});
}, // 模拟对话框
showModal: function (matter : any) {
return new Promise((resolve, _reject) => {
uni.showModal({
title: matter.title || '',
content: matter.content || '',
// 是否显示取消按钮,默认为 true
showCancel: matter.showCancel,
// 取消按钮的文字,默认为"取消"
cancelText: matter.cancelText || "取消",
// 取消按钮的文字颜色,默认为"#000000"
cancelColor: matter.cancelColor || "#000000",
// 确定按钮的文字,默认为"确定"
confirmText: matter.confirmText || "确定",
/*
确定按钮的文字颜色,H5平台默认为"#007aff",
微信小程序平台默认为"#576B95",
百度小程序平台默认为 "#3c76ff"
*/
// confirmColor: matter.confirmColor || '#576B95',
success: (res) => {
if (res.confirm) {
// console.log('用户点击确定');
resolve(res)
}
// if (res.cancel) {
// console.log('用户点击取消');
// reject(res.cancel)
// }
}
})
})
}
}

operate.ts

api.ts 代码如下

import {
request
} from '@/utils/requset' // 手机密码登录
export const text = function (data : any) {
return request({
url: "/pai/api/pai",
method: "POST",
hideLoading: true,
data: data,
})
} /*
使用方法: 在请求页面中调用 1.先导入本页面
import {text} from '@/common/api' 2.在methods 中 调用:
text().then((res) => {
console.log(res);
}) */

api.ts

2.utils 中创建 requset.ts

配置 uni.request 统一请求, uni.getNetworkType 判断当前网络状态

通过 uni.addInterceptor 拦截器,实现请求前后的数据监听(该方法只写了监听,具体逻辑项目没用到)

import route from '@/utils/routeBlocker'  路由封装-方法链接

无感刷新 token 配置,具体代码如下

import operate from "@/common/operate"
import store from '@/store-ts/index'
import route from '@/utils/routeBlocker' /*
解决: 类型“string | AnyObject | ArrayBuffer”上不存在属性“code”。
类型“string”上不存在属性“code”。
*/
interface Codeable {
data : Object | String | ArrayBuffer,
code : Number,
} //请求对列 / 请求状态
let requestQueue = [],
isRefreshing = false; export const request = function (param : any) {
//请求参数
let url = param.url,
method = param.method,
header = {},
data = param.data || {},
hideLoading = param.hideLoading || false; //拼接完整请求地址
let requestUrl = operate.api() + url; //跨域解决
// let requestUrl = url;
// console.log(requestUrl) //请求方式:GET或POST
if (method) {
method = method.toUpperCase(); //小写改为大写
if (method == "POST") {
header = {
// 'content-type': 'application/x-www-form-urlencoded',
'content-type': "application/json",
};
} else {
header = {
'content-type': "application/json",
};
}
}
// 拼接header 登录参数
let jointHeader = Object.assign({}, header, operate.commonBeg()); //用户交互:加载圈
if (!hideLoading) {
uni.showLoading({
title: '加载中...',
mask: true
});
} // 请求-拦截器
// requestBlocker() //开始请求
return new Promise((resolve, reject) => {
// 判断有无网络验证
noneNetwork().then(() => {
//请求放到promise队列,等待更新token后重新调用。
addRequestQueue(url, method, param.data, jointHeader) //更新 token
flushToken().then(() => {
// 执行等待的请求
onRefreshed().then(resolve).catch(reject);
})
}).catch(() => {
//隐藏加载
if (!hideLoading) {
uni.hideLoading();
}
}) //开始请求
uni.request({
url: requestUrl,
data: data,
method: method,
header: jointHeader,
success(res) {
let data = res.data as Codeable // code判断: 刷新令牌
if (data.code == 401) {
// 处理token刷新
if (!isRefreshing) {
isRefreshing = true
//请求放到promise队列,等待更新token后重新调用。
addRequestQueue(url, method, param.data, jointHeader) //更新 token
flushToken().then(() => {
// 执行等待的请求
onRefreshed().then(resolve).catch(reject);
})
}
return;
} // code判断: 重新登录
if (data.code == 403) {
restartLogin()
return;
} // 将结果抛出
resolve(data)
},
//请求失败
fail: (err) => {
operate.toast({
title: '网络连接错误',
icon: 'loading'
})
// 将结果抛出
reject(err)
/*
.catch(err=>{
console.log(err)
})
*/
},
//请求完成
complete() {
//隐藏加载
if (!hideLoading) {
uni.hideLoading();
}
}
})
})
} // 执行等待的请求
const onRefreshed = () => {
return new Promise((resolve, reject) => {
let item = requestQueue.shift();
// console.warn('执行等待的请求', item);
request({
url: item.url,
method: item.method,
hideLoading: true,
data: item.data,
}).then(resolve).catch(reject)
});
} // 添加请求到队列
const addRequestQueue = (url : string, method : object, data : object, header : object) => {
requestQueue.push({
url,
method,
data,
header
})
} /**
* @description: 登录刷新 token 请求接口
* @return
*/
export const flushToken = function () {
return new Promise((resolve, errs) => {
uni.request({
url: operate.api() + '/app-api/refresh-token-刷新接口地址',
method: 'POST',
header: {
'Content-Type': 'application/json'
},
data: {
refreshToken: store.getters['flushToken'],
},
success(res) {
// console.warn('刷新令牌', res.data);
let data = res.data as any if (data.code == 0) {
//登录刷新
store.commit("user/REFRESH_TOKEN", {
accessToken: data.data.accessToken,
refreshToken: data.data.refreshToken
}); resolve('刷新令牌成功')
return
} //登录 失效
if (data.code == 401) {
operate.showModal({
title: '您的登陆已过期',
confirmText: '重新登录',
showCancel: false,
}).then((_res) => {
//清除登录信息
store.commit('user/LOG_OUT');
//去登录页
restartLogin(false)
})
}
},
fail: (err) => {
console.error('刷新令牌失败', err);
errs(err)
},
complete() {
isRefreshing = false
}
})
})
} /**
* @description: 请求-拦截器
* @return
* 通过拦截器,实现请求前后的数据监听
*/
// const requestBlocker = function () {
// uni.addInterceptor('request', {
// invoke(args) {
// console.log("求前后的数据监听",args);
// }
// })
// } /**
* @description: 判断有无网络验证
* @return
*/
const noneNetwork = function () {
return new Promise((resolve, reject) => {
uni.getNetworkType({
success(res) {
if (res.networkType == 'none') {
uni.showModal({
title: '没有网络',
content: '请检查您的网络',
showCancel: false,
success: (_res) => {
resolve("无网络确定-返回")
}
});
}
},
complete() {
reject('取消-加载圈')
}
})
})
} /**
* @description: 重新登录(统一方法)
* @return
*/
const restartLogin = function (toastShow = true) {
if (toastShow) {
operate.toast({
title: "登录超时!请重新登录"
})
}
setTimeout(() => {
route({
url: '/pages/logIn/logIn',
type: "navigateTo",
login: false,
})
}, 500)
}

requset.ts

缺点:在多个请求同时进行,会出现多次调用(刷新token)的情况。

当前使用 isRefreshing 判断请求刷新转态,没结束不能在调用刷新接口,对于复数请求,还是会多次请求刷新。

下图测试为,同时请求4个接口

项目地址:https://gitee.com/jielov/uni-app-tabbar

uniapp请求封装-token无感刷新的更多相关文章

  1. 无感刷新 Token

    什么是JWT JWT是全称是JSON WEB TOKEN,是一个开放标准,用于将各方数据信息作为JSON格式进行对象传递,可以对数据进行可选的数字加密,可使用RSA或ECDSA进行公钥/私钥签名. 使 ...

  2. Spring Cloud实战 | 最八篇:Spring Cloud +Spring Security OAuth2+ Axios前后端分离模式下无感刷新实现JWT续期

    一. 前言 记得上一篇Spring Cloud的文章关于如何使JWT失效进行了理论结合代码实践的说明,想当然的以为那篇会是基于Spring Cloud统一认证架构系列的最终篇.但关于JWT另外还有一个 ...

  3. axios实现无感刷新

    前言 最近在做需求的时候,涉及到登录token,产品提出一个问题:能不能让token过期时间长一点,我频繁的要去登录. 前端:后端,你能不能把token 过期时间设置的长一点. 后端:可以,但是那样做 ...

  4. 基于OAuth2.0的token无感知刷新

    目前手头的vue项目关于权限一块有一个需求,其实架构师很早就要求我做了,但是由于这个紧急程度不是很高,最近临近项目上线,我才想起,于是赶紧补上这个功能.这个项目是基于OAuth2.0认证,需要在每个请 ...

  5. 请求时token过期自动刷新token

    1.在开发过程中,我们都会接触到token,token的作用是什么呢?主要的作用就是为了安全,用户登陆时,服务器会随机生成一个有时效性的token,用户的每一次请求都需要携带上token,证明其请求的 ...

  6. uni-app 请求封装

    1.创建一个http.js ​ const baseUrl = 'http://192.168.1.188:8080'; const httpRequest = (opts, data) => ...

  7. Axios 网络请求组件封装 (鉴权、刷新、拦截)

    一.前言 注意:本教程需要你对axios有一定的了解,不适用于小白(只能借鉴,希望你能自己动手),注释都写的很清楚.此封装并非完整版,已进行部分删减修改操作,但仍然适用于大部分业务场景,如果不适用于你 ...

  8. Flutter 实际开发常用工具类(全局提示,请求封装,token缓存,验证码倒计时、常用窗帘动画及布局)

    介绍: 一星期从入门到实际开发经验分享及总结           代码传送门github Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面.未来App开发 ...

  9. drf:restful概念,类继承关系,drf请求封装,drf请求流程,版本控制组件,认证组件(token),权限组件

    1.restful规范 resfful规范的概念最重要: 是一套规范,规则,用于程序之间进行数据交换的约定. 他规定了一些协议,对我们感受最直接的就是,以前写增删改查的时候需要些四个视图寒素,rest ...

  10. uniapp 基于 flyio 的 http 请求封装

    之前写请求都是用别人封装好的,直接 import request 完事,自己第一次写还是一头雾水,学习了一波搞清楚了些,可以写简单的封装了. 首先要搞清楚为什么封装请求,同其他的封装一样,我们把不同请 ...

随机推荐

  1. 函数(C语言)

    目录 1. 函数的概念 2. 库函数 2.1 标准库和头文件 2.2 库函数的使用方法 3. 自定义函数 3.1 函数的语法形式 3.2 函数的举例 4. 形参和实参 4.1 实参 4.2 形参 4. ...

  2. 《HelloGitHub》第 103 期

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. github.com/521xueweihan/HelloG ...

  3. 掌控物体运动艺术:图扑 Easing 函数实践应用

    现如今,前端开发除了构建功能性的网站和应用程序外,还需要创建具有吸引力且尤为流畅交互的用户界面,其中动画技术在其中发挥着至关重要的作用.在数字孪生领域,动画的应用显得尤为重要.数字孪生技术通过精确模拟 ...

  4. MyBatis-Plus条件构造器:构建安全、高效的数据库查询

    一.关于条件构造器(Wrapper) 1.1 简介 MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件.Wrapper 类允许开发者以链式调用的方式构 ...

  5. 2.16 Linux挂载详解

    前面讲过,Linux 系统中"一切皆文件",所有文件都放置在以根目录为树根的树形目录结构中.在 Linux 看来,任何硬件设备也都是文件,它们各有自己的一套文件系统(文件目录结构) ...

  6. markdown小小白常用语法

    第一次用vscode写笔记去同步Cnblog,不知道写啥就记点常用的md语法吧 1. 标题怎么写? 利用"#" + " " 即可实现第几节标题(其中'/',表转 ...

  7. jQuery.validator验证无效的可能原因

    最近用jQuery.validator做表单的前端验证,却发现验证规则都无效.最后发现以下原因会导致校验无效 1.jquery.min.js重复引用. 2.js中有bug存在. 3.<input ...

  8. Solr 学习(5) —- Solr查询语法和参数

    1.查询地址 建立好solr的索引后,可以通过管理界面进行查询.http://127.0.0.1:8983/solr/admin/form.jsp 要尝试多个查询方法的话,可以进入full inter ...

  9. 如何使用Python编写一个Lisp解释器

    原文出处: Peter Norvig   译文出处: jnjc(@jnjcc) 本文有两个目的: 一是讲述实现计算机语言解释器的通用方法,另外一点,着重展示如何使用Python来实现Lisp方言Sch ...

  10. Java深度历险(一)——Java字节代码的操纵

    [编者按]Java作为业界应用最为广泛的语言之一,深得众多软件厂商和开发者的推崇,更是被包括Oracle在内的众多JCP成员积极地推动发展.但是对于Java语言的深度理解和运用,毕竟是很少会有人涉及的 ...