企业级网站开发与部署wordpress网站静态化
web/
2025/9/29 19:01:42/
文章来源:
企业级网站开发与部署,wordpress网站静态化,ps网站轮播图怎么做,手机可以做网站的服务器吗前言
关于axios全局loading的封装博主已经发过一次了#xff0c;这次是在其基础上增加了token的无感刷新。
token无感刷新流程
首次登录的时候会获取到两个token#xff08;AccessToken#xff0c;RefreshToken#xff09;持久化保存起来#xff08;localStorage方案这次是在其基础上增加了token的无感刷新。
token无感刷新流程
首次登录的时候会获取到两个tokenAccessTokenRefreshToken持久化保存起来localStorage方案正常请求业务接口的时候携带AccessToken当接口口返回401权限错误时使用RefreshToken请求接口获取新的AccessToken替换原有旧的AccessToken并保存继续未完成的请求携带AccessTokenRefreshToken也过期了跳转回登录页面重新登录
后端设计
这里采用node简单实现的后台接口服务
后端存有两个字段分别保存长短token并且每一段时间更新他们短token过期返回 returncode:104长token过期返回 returncode: 108请求成功返回returncode: 0请求头中pass用来接收客户端长token请求头中authorization用来接收客户端短token
1、创建一个新文件夹通过vscode打开运行
npm init -y 2、安装koa
npm i koa -s 3、安装nodemon
npm i nodemon -g 4、使用路由中间件
npm i koa-router -S 5、跨域处理
npm i koa2-cors 6、新建routes/index.js
const router require(koa-router)();
let accessToken init_s_token; //短token
let refreshToken init_l_token; //长token/* 5s刷新一次短token */
setInterval(() {accessToken s_tk Math.random();
}, 5000);/* 一小时刷新一次长token */
setInterval(() {refreshToken l_tk Math.random();
}, 600000);/* 登录接口获取长短token */
router.get(/login, async (ctx) {ctx.body {returncode: 0,accessToken,refreshToken,};
});/* 获取短token */
router.get(/refresh, async (ctx) {//接收的请求头字段都是小写的let { pass } ctx.headers;if (pass ! refreshToken) {ctx.body {returncode: 108,info: 长token过期重新登录,};} else {ctx.body {returncode: 0,accessToken,};}
});/* 获取应用数据1 */
router.get(/getData, async (ctx) {let { authorization } ctx.headers;if (authorization ! accessToken) {ctx.body {returncode: 104,info: token过期,};} else {ctx.body {code: 200,returncode: 0,data: { id: Math.random() },};}
});/* 获取应用数据2 */
router.get(/getData2, async (ctx) {let { authorization } ctx.headers;if (authorization ! accessToken) {ctx.body {returncode: 104,info: token过期,};} else {ctx.body {code: 200,returncode: 0,data: { id: Math.random() },};}
});module.exports router;
7、创建index.js文件
const Koa require(koa)
const app new Koa();
const index require(./routes/index)const cors require(koa2-cors);app.use(cors());app.use(index.routes(),index.allowedMethods())app.listen(4000,() {console.log(server is listening on port 4000)
})
8、配置package.json
dev:nodemon index.js, 9、运行 npm run dev这时服务端已准备好
npm run dev前端源码
interceptors.ts
/** axios封装* 请求拦截、相应拦截、错误统一处理*/
import Axios from axios;
import { ElMessage, ElLoading } from element-plus;
import _ from lodash;
import router from /router;
import BaseRequest from /request/request;
const axios Axios.create({//baseURL: localStorage.getItem(address)?.toString(), // url base url request url// timeout: 50000 // request timeout
});
// loading对象
let loadingInstance: { close: () void } | null;
// 变量isRefreshing
let isRefreshing false;
// 后续的请求队列
let requestList: ((newToken: any) void)[] [];
// 请求合并只出现一次loading
// 当前正在请求的数量
let loadingRequestCount 0;
// post请求头
axios.defaults.headers.post[Content-Type] application/json;charsetUTF-8;
// request interceptoraxios.interceptors.request.use((config: any) {let loadingTarget body;if (config.headers.loadingTarget) {loadingTarget config.headers.loadingTarget;}const isShowLoading config.headers.isShowLoading;const target document.querySelector(loadingTarget);if (target !isShowLoading) {// 请求拦截进来调用显示loading效果showLoading(loadingTarget);}// do something before request is sent// if (sessionStorage.getItem(token)) {// config.headers.Authorization // Bearer sessionStorage.getItem(token); // 让每个请求携带自定义 token 请根据实际情况自行修改// }if (config.url) {// 此处为 Refresh Token 专用接口请求头使用 Refresh Tokenif (config.url.indexOf(/refresh) 0) {config.headers.Authorization localStorage.getItem(RefreshToken);} else if (!(config.url.indexOf(/login) ! -1)) {// 其他接口请求头使用 Access Tokenconfig.headers.Authorization localStorage.getItem(accessToken);}}return config;},(error) {// do something with request errorconsole.log(error); // for debugreturn Promise.reject(error);}
);
// http response 拦截器
axios.interceptors.response.use(async (response) {setTimeout(() {hideLoading();}, 200);const data response.data;if (data.code 401) {// 控制是否在刷新token的状态if (!isRefreshing) {// 修改isRefreshing状态isRefreshing true;// 这里是获取新token的接口方法在这里省略了。const url /refresh;const BaseRequestFun new BaseRequest(url, );BaseRequestFun.get().then(async (res) {if (res res.accessToken) {console.log(a);// 新tokenconst newToken res.accessToken;// 保存新的accessTokenlocalStorage.setItem(accessToken, newToken);// 替换新accessTokenresponse.config.headers.Authorization newToken;// token 刷新后将数组里的请求队列方法重新执行requestList.forEach((cb) cb(newToken));// 重新请求完清空requestList [];// 继续未完成的请求const resp await axios.request(response.config);// 重置状态isRefreshing false;// 返回请求结果return resp;} else {// 清除tokenlocalStorage.clear();// 重置状态isRefreshing false;// 跳转到登录页router.replace(/);}});} else {// 后面的请求走这里排队// 返回未执行 resolve 的 Promisereturn new Promise((resolve) {// 用函数形式将 resolve 存入等待获取新token后再执行requestList.push((newToken) {response.config.headers.Authorization newToken;resolve(axios(response.config));});});}}return data;},(err) {setTimeout(() {hideLoading();}, 200);// 返回状态码不为200时候的错误处理ElMessage({message: err.toString(),type: error,duration: 5 * 1000,});return Promise.resolve(err);}
);
// 显示loading的函数 并且记录请求次数
const showLoading (target: any) {if (loadingRequestCount 0) {loadingInstance ElLoading.service({lock: true,text: 加载中...,target: target,background: rgba(255,255,255,0.5),});}loadingRequestCount;
};// 隐藏loading的函数并且记录请求次数
const hideLoading () {if (loadingRequestCount 0) return;loadingRequestCount--;if (loadingRequestCount 0) {toHideLoading();}
};// 防抖将 300ms 间隔内的关闭 loading 便合并为一次. 防止连续请求时, loading闪烁的问题。
const toHideLoading _.debounce(() {// eslint-disable-next-line typescript-eslint/ban-ts-comment// ts-ignoreloadingInstance.close();loadingInstance null;
}, 300);export default axios;
request.ts
import instance from ./interceptors;
import { ElMessage } from element-plus;export default class baseRequest {private url: any;private params: any;constructor(url: any, params: any) {this.url url;this.params typeof params undefined ? {} : params;}get(...params: any[]) {return instance.get(this.url, {params: this.params,headers: {loadingTarget: params[0],isShowLoading: params[1] undefined ? true : params[1],},}).then((res: any) {if (res.code 200) {return Promise.resolve(res);} else {ElMessage({message: res.entitys[Object.keys(res.entitys)[0]],type: error,duration: 5 * 1000,});return Promise.resolve(false);}}).catch((e) {ElMessage({message: e,type: error,duration: 5 * 1000,});Promise.resolve(false);});}post(...params: any[]) {return instance.post(this.url, this.params, {headers: {loadingTarget: params[0],isShowLoading: params[1] undefined ? true : params[1],},}).then((res: any) {if (res.code 200) {return Promise.resolve(res.entitys);} else {ElMessage({message: res.entitys[Object.keys(res.entitys)[0]],type: error,duration: 5 * 1000,});Promise.resolve(false);}}).catch((e) {ElMessage({message: e,type: error,duration: 5 * 1000,});Promise.resolve(false);});}put(...params: any[]) {return instance.put(this.url, this.params, {headers: {loadingTarget: params[0],isShowLoading: params[1] undefined ? true : params[1],},}).then((res: any) {if (res.code 200) {return Promise.resolve(res.entitys);} else {ElMessage({message: res.entitys[Object.keys(res.entitys)[0]],type: error,duration: 5 * 1000,});Promise.resolve(false);}}).catch((e) {ElMessage({message: e,type: error,duration: 5 * 1000,});Promise.resolve(false);});}delete(...params: any[]) {return instance.delete(this.url, {params: this.params,headers: {loadingTarget: params[0],isShowLoading: params[1] undefined ? true : params[1],},}).then((res: any) {if (res.code 200) {return Promise.resolve(res.entitys);} else {ElMessage({message: res.entitys[Object.keys(res.entitys)[0]],type: error,duration: 5 * 1000,});Promise.resolve(false);}}).catch((e) {ElMessage({message: e,type: error,duration: 5 * 1000,});Promise.resolve(false);});}upfile(...params: any[]) {return instance.post(this.url, this.params, {headers: {Content-Type: multipart/form-data,X-Requested-With: XMLHttpRequest,loadingTarget: params[0],isShowLoading: params[1] undefined ? true : params[1],},}).then((res: any) {if (res.code 200) {return Promise.resolve(res.entitys);} else {ElMessage({message: res.entitys[Object.keys(res.entitys)[0]],type: error,duration: 5 * 1000,});Promise.resolve(false);}}).catch((e) {ElMessage({message: e,type: error,duration: 5 * 1000,});Promise.resolve(false);});}downfile(...params: any[]) {return instance.post(this.url, this.params, { responseType: blob }).then((res: any) {const fileReader new FileReader();fileReader.onload function (e: any) {try {const jsonData JSON.parse(e.target.result); // 说明是普通对象数据后台转换失败if (jsonData.code) {ElMessage({message: jsonData.message,type: error,duration: 5 * 1000,});Promise.resolve(false);}} catch (err) {// 解析成对象失败说明是正常的文件流const url window.URL.createObjectURL(res);const eleLink document.createElement(a);eleLink.href url;eleLink.download params[2];// eleLink.download 1.xls;document.body.appendChild(eleLink);eleLink.click();window.URL.revokeObjectURL(url);}};fileReader.readAsText(res);}).catch((e) {ElMessage({message: e,type: error,duration: 5 * 1000,});Promise.resolve(false);});}icd9Export() {return instance.post(this.url, this.params, { responseType: blob }).then((res: any) {const fileReader new FileReader();fileReader.onload function (e: any) {try {const jsonData JSON.parse(e.target.result); // 说明是普通对象数据后台转换失败if (jsonData.code) {ElMessage({message: jsonData.message,type: error,duration: 5 * 1000,});Promise.resolve(false);}} catch (err) {// 解析成对象失败说明是正常的文件流const url window.URL.createObjectURL(res);const eleLink document.createElement(a);eleLink.href url;eleLink.download icd9.xls;document.body.appendChild(eleLink);eleLink.click();window.URL.revokeObjectURL(url);}};fileReader.readAsText(res);}).catch((e) {ElMessage({message: e,type: error,duration: 5 * 1000,});Promise.resolve(false);});}icd10Export() {return instance.post(this.url, this.params, { responseType: blob }).then((res: any) {const fileReader new FileReader();fileReader.onload function (e: any) {try {const jsonData JSON.parse(e.target.result); // 说明是普通对象数据后台转换失败if (jsonData.code) {ElMessage({message: jsonData.message,type: error,duration: 5 * 1000,});Promise.resolve(false);}} catch (err) {// 解析成对象失败说明是正常的文件流const url window.URL.createObjectURL(res);const eleLink document.createElement(a);eleLink.href url;eleLink.download icd10.xls;document.body.appendChild(eleLink);eleLink.click();window.URL.revokeObjectURL(url);}};fileReader.readAsText(res);}).catch((e) {ElMessage({message: e,type: error,duration: 5 * 1000,});Promise.resolve(false);});}
}
测试vue
templatedivel-button typeprimary clicklogin()登录/el-buttonel-button typeprimary clickgetData()接口一/el-buttonel-button typeprimary clickgetData2()接口二/el-button/div
/templatescript langts setup
import BaseRequest from /request/request;
const login () {const url /login;const BaseRequestFun new BaseRequest(url, );BaseRequestFun.get().then((res) {if (res) {console.log();localStorage.setItem(accessToken, res.accessToken);localStorage.setItem(RefreshToken, res.refreshToken);}});
};
const getData () {const url /getData;const BaseRequestFun new BaseRequest(url, );BaseRequestFun.get().then((res) {if (res) {console.log(res);}});
};
const getData2 () {const url /getData2;const BaseRequestFun new BaseRequest(url, );BaseRequestFun.get().then((res) {if (res) {console.log(res);}});
};
/scriptstyle langscss/style
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/84050.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!