一、需求:
-
在发送请求之前,先判断用户是否有token,没有就执行登陆请求,将token保存,然后再执行原来请求;
-
拥有token,就直接执行请求;但是用户的这个token可能是过期的,如果执行请求发现用户登陆过期,就统一返回40001,然后对40001的响应统一处理,执行登陆请求,再执行原来请求。
-
最终实现用户无感登陆的体验效果。
二、流程图如下:
三、主要代码
/**
* 请求拦截器:
* 在这里实现的作用是将所有请求前判断用户是否授权获取用户信息
* @param {*} config
*/
function requestInterceptor(config) {
console.log("经过了请求拦截器")
return new Promise((resolve, reject) => {
if (!config.header.authorization) {
userLogin().then(res =>{
if(res){
config.header.authorization = wx.getStorageSync('userInfo').token
resolve(config);
}
})
} else {
resolve(config);
}
});
}
// 响应拦截器
function responseInterceptor(response) {
console.log("经过响应拦截器")
return new Promise((resolve, reject) => {
// 处理响应结果
if (response.data.flag) {
resolve(response);
} else {
if (response.data.code === 40001) {
userLogin().then(res => {
reject(response)
})
} else {
wx.showToast({
title: response.data.message,
icon: "error",
duration: 2000
})
}
}
});
}
其中封装的一个post请求,带请求头
其他请求的封装方法完成类似,懂一个其他就都懂了。
四、完整代码:
var tokenKey = "userInfo"; // 将登陆凭证储存以key为“token”储存在本地
var serverUrl = "http://localhost:8088/wechat"; // 2020
// 例外不用token的地址
// var exceptionAddrArr = ['/user/login', ];
var exceptionAddrArr = [];
//请求头处理函数
function CreateHeader(url, type) {
let header = {}
if (type == 'POST_PARAMS') {
header = {
'content-type': 'application/x-www-form-urlencoded'
}
} else {
header = {
'content-type': 'application/json'
}
}
if (exceptionAddrArr.indexOf(url) == -1) { //排除请求的地址不须要token的地址
let token = wx.getStorageSync(tokenKey).token;
// header.Authorization = token;
header['authorization'] = token;
}
return header;
}
/**
* 请求拦截器:
* 在这里实现的作用是将所有请求前判断用户是否授权获取用户信息
* @param {*} config
*/
function requestInterceptor(config) {
console.log("经过了请求拦截器")
return new Promise((resolve, reject) => {
if (!config.header.authorization) {
userLogin().then(res =>{
if(res){
config.header.authorization = wx.getStorageSync('userInfo').token
resolve(config);
}
})
} else {
resolve(config);
}
});
}
// 响应拦截器
function responseInterceptor(response) {
console.log("经过响应拦截器")
return new Promise((resolve, reject) => {
// 处理响应结果
if (response.data.flag) {
resolve(response);
} else {
if (response.data.code === 40001) {
userLogin().then(res => {
reject(response)
})
} else {
wx.showToast({
title: response.data.message,
icon: "error",
duration: 2000
})
}
}
});
}
/**
* 封装wx.getUserProfile()方法
*/
function wxGetUserProfile() {
return new Promise((resolve, reject) => {
wx.getUserProfile({
desc: '获取你的昵称、头像、地区及性别',
success: (res) => {
let userInfo = {
userName: res.userInfo.nickName,
iconUrl: res.userInfo.avatarUrl
}
wx.setStorageSync('userInfo', userInfo)
resolve(userInfo)
},
fail: (res) => {
reject(res)
}
})
})
}
/**
* 封装wx.login
*/
function wxLogin() {
return new Promise((resolve, reject) => {
wx.login({
success: (res) => {
console.log("wxLogin()获取验证码:" + res.code)
resolve(res.code)
},
fail: (res) => {
reject(res)
}
})
})
}
/**
* 封装后端登陆方法
* @param {验证码} code
*/
function mpLogin(data) {
return new Promise((resolve, reject) => {
wx.request({
url: serverUrl + '/user/login',
data: data,
method: 'POST',
success: (res => {
resolve(res.data)
}),
fail: (res => {
reject(res)
}),
})
})
}
/**
* 调用wx.login 和 mplogin 完成用户后端登陆
*/
async function userLogin() {
let userInfo = wx.getStorageSync('userInfo');
if (!userInfo) {
userInfo = await wxGetUserProfile()
}
if(!userInfo){
return;
}
let code = await wxLogin();
let data = {
code: code,
userName: userInfo.userName,
iconUrl: userInfo.iconUrl
}
return new Promise((resolve, reject) => {
mpLogin(data).then(res => {
if (res.flag) {
console.log("userLogin()登陆成功返回信息:" + res)
wx.setStorageSync('userInfo', res.data)
resolve(true)
} else {
wx.showToast({
title: res.message,
icon: "error",
duration: 2000
})
resolve(false)
}
})
})
}
//post请求,数据按照query方式传给后端
/**
*
* @param {请求地址} url
* @param {请求数据} data
* @param {重试次数} times
*/
function postRequest(url, data = {}, times) {
// 获取请求头
let header = CreateHeader(url, 'POST');
return new Promise((resolve, reject) => {
const config = {
url: serverUrl + url,
data: data,
header: header,
method: 'POST',
success: (res => {
// 对响应统一处理
responseInterceptor(res)
.then(res => {
resolve(res.data);
}).catch(res => {
// 重
if (times > 0) {
postRequest(url, data, times - 1).then(res => {
resolve(res)
})
} else {
wx.showToast({
title: '请稍后再试',
icon: "loading",
})
}
})
}),
fail: (res => {
reject(res)
}),
}
// 请求拦截器
requestInterceptor(config)
.then(config => {
wx.request(config);
}).catch(error => {
reject(error);
});
})
}
//get 请求
function getRequest(url, data, times) {
let header = CreateHeader(url, 'GET');
return new Promise((resolve, reject) => {
const config = {
url: serverUrl + url,
data: data,
header: header,
method: 'GET',
success: (res => {
responseInterceptor(res)
.then(res => {
resolve(res.data);
}).catch(res => {
// 重
if (times > 0) {
getRequest(url, data, times - 1).then(res => {
resolve(res)
})
} else {
wx.showToast({
title: '请稍后再试',
icon: "loading",
})
}
})
}),
fail: (res => {
reject(res)
})
}
// 请求拦截器
requestInterceptor(config)
.then(config => {
wx.request(config);
}).catch(error => {
reject(error);
});
})
}
//put请求
function putRequest(url, data, times) {
let header = CreateHeader(url, 'PUT');
return new Promise((resolve, reject) => {
const config = {
url: serverUrl + url,
data: data,
header: header,
method: 'PUT',
success: (res => {
responseInterceptor(res)
.then(res => {
resolve(res.data);
}).catch(res => {
// 重
if (times > 0) {
putRequest(url, data, times - 1).then(res =>{
resolve(res)
})
} else {
wx.showToast({
title: '请稍后再试',
icon: "loading",
})
}
})
}),
fail: (res => {
reject(res)
})
}
})
}
//delete请求
function deleteRequest(url, data, times) {
let header = CreateHeader(url, 'DELETE');
return new Promise((resolve, reject) => {
const config = {
url: serverUrl + url,
data: data,
header: header,
method: 'DELETE',
success: (res => {
responseInterceptor(res)
.then(res => {
resolve(res.data);
}).catch(res => {
if (times > 0) {
deleteRequest(url, data, times - 1).then(res =>{
resolve(res)
})
} else {
wx.showToast({
title: '请稍后再试',
icon: "loading",
})
}
})
}),
fail: (res => {
reject(res)
})
}
// 请求拦截器
requestInterceptor(config)
.then(config => {
wx.request(config);
}).catch(error => {
reject(error);
});
})
}
//导入
module.exports = {
getRequest: getRequest,
postRequest: postRequest,
putRequest: putRequest,
deleteRequest: deleteRequest,
userLogin: userLogin,
}
五、使用示范:
const re = require('../../utils/request.js'); // 导入
Page({
btCreateSubject(e){
re.postRequest("/subject/create",{
subjectName:"学习强国3"
},2).then(res =>{
console.log("创建科目成功返回数据:")
console.log(res)
})
},
})
-
在用户没有token的情况下:
- 进行请求拦截器,调用了登陆方法:
- 执行原来的操作,得到结果
- 进行请求拦截器,调用了登陆方法:
-
在用户有token的情况下,但是服务器已经没有维护用户的登陆状态了
-
redis当中没有维护用户状态了
-
原本请求–>登陆请求–>原本请求
文章来源:https://www.toymoban.com/news/detail-649509.html -
后端已经维持用户登陆状态
文章来源地址https://www.toymoban.com/news/detail-649509.html
-
总结:
- 虽然这样封装看起来挺复杂的,但是对于用户来说是一种很好的体验。对于普通的网站,如果发现你你的登陆过期,又得重新输入密码注册。但是这样我们无全不需要,只要你已经是登陆过的用户,除非你清除了小程序的缓存,否则在你接下来使用过程中都不需要再手机登陆。
- 其次,对于前端js编写来说,不需要在每个请求前都去判断用户是否本地缓存有token,都在由请求拦截器统一处理。对于请求后的响应,我们不需要去处理各种情况,只需要处理成功的情况就可以了。
关于实现小程序用户“无感”登陆的方法,我相信这不是最优解,但是这是我目前能想到最好的解决办法了。如果小伙伴有更好的登陆流程,欢迎在评论区告诉我,一起讨论~
到了这里,关于微信小程序封装request请求,包含请求拦截器,响应拦截器和请求重试功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!