当前是vue3+ts版本的封装
vue3+js版本请求封装可参考 https://www.cnblogs.com/lovejielive/p/14343619.html
token无感刷新,可自行删除 requset.ts 中 ts 相关数据恢复vue2版本
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,return,请求,url,res,method,token,data,无感 From: https://www.cnblogs.com/lovejielive/p/18643655