kkfile学习笔记
1.epub是运用epub.js来执行预览,后端只需要将数据以blob的形式传给前端,前端加载epub.js,页面就渲染出epub的内容。
2.xlsx文件是通过JODConverter依赖类来执行转换的,它可以把office文件转换为pdf、html等文件。jobconverter文档详解_jodconverter-优快云博客 ,先转换为html文件,再将其以流的形式输出到前端,并在其中添加了excel.header.js文件,然后可以显示编辑的功能了。
芋道源码的uniapp在token过期循环刷新的问题
芋道源码的uniapp中,用户第一次登录以后,系统会生成并返回两个token,一个是accessToken。accesstoken的过期时间是半个小时。另一个是resfreshToken,refreshToken的过期时间是12小时。在后续的请求中拦截器会自动把accesstoken加载到请求头上,后台接收到请求以后首先判断token是否存在且没有过期,如果过期,就返回401,前台接到401以后,就会发起refreshToken的请求,将refreshToken传入,后台接收到这个请求以后,判断refreshToken是否存在且没有过期,如验证不通过,返回的code!=1,前端就弹出重新登录的窗口;如果验证通过,就重新生成新的accesstoken,返回给前端;按理说,前端应该将这个新的token存储到本地,但正是因为之前的代码中没有对token进行存储,导致本地还是以前的token,然后系统请求的时候还是用的已过期的token,所以再次访问失败,陷入不断请求失败和刷新token的死循环中。解决的方案就是在refreshToken以后,把获取到的新的token存储到本地,这样就下次的请求就不会报401,代码如下:
/**
* @description 响应拦截器
*/
http.interceptors.response.use(
(response) => {
// 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌
// if (response.config.url.indexOf('/member/auth/') >= 0 && response.data?.data?.accessToken) {
// $store('user').setToken(response.data.data.accessToken, response.data.data.refreshToken);
// }
// 自定处理【loading 加载中】:如果需要显示 loading,则关闭 loading
response.config.custom.showLoading && closeLoading();
// 自定义处理【error 错误提示】:如果需要显示错误提示,则显示错误提示
if (response.data.code !== 0) {
// 特殊:如果 401 错误码,则跳转到登录页 or 刷新令牌
if (response.data.code === 401) {
return refreshToken(response.config);
}
// 错误提示
if (response.config.custom.showError) {
uni.showToast({
title: response.data.msg || '服务器开小差啦,请稍后再试~',
icon: 'none',
mask: true,
});
}
}
// 其他代码
}
let requestList = [] // 请求队列
let isRefreshToken = false // 是否正在刷新中
const refreshToken = async (config) => {
// 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error)
if (config.url.indexOf('/member/auth/refresh-token') >= 0) {
return Promise.reject('error')
}
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) {
isRefreshToken = true
// 1. 如果获取不到刷新令牌,则只能执行登出操作
const refreshToken = getRefreshToken()
if (!refreshToken) {
return handleAuthorized()
}
// 2. 进行刷新访问令牌
try {
const refreshTokenResult = await AuthUtil.refreshToken(refreshToken);
if (refreshTokenResult.code !== 0) {
// 如果刷新不成功,直接抛出 e 触发 2.2 的逻辑
// noinspection ExceptionCaughtLocallyJS
throw new Error('刷新令牌失败');
}
//!!!!!!!不加这个代码的话,后续的请求还是在用原来的token,然后就还是报token失效,
uni.setStorageSync('token', refreshTokenResult.data.accessToken);
// 2.1 刷新成功,则回放队列的请求 + 当前请求
config.header.Authorization = 'Bearer ' + getAccessToken()
requestList.forEach((cb) => {
cb()
})
requestList = []
return request(config)
} catch (e) {
// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
// 2.2 刷新失败,只回放队列的请求
requestList.forEach((cb) => {
cb()
})
// 提示是否要登出。即不回放当前请求!不然会形成递归
return handleAuthorized()
} finally {
requestList = []
isRefreshToken = false
}
} else {
// 添加到队列,等待刷新获取到新的令牌
return new Promise((resolve) => {
requestList.push(() => {
config.header.Authorization = 'Bearer ' +
getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(request(config))
})
})
}
}
备注:后端代码在生成token以后,数据库和redis都会存一份,在后续查询的时候,是查的redis