微信小程序封装request请求,包含请求拦截器,响应拦截器和请求重试功能

  • Post author:
  • Post category:小程序




一、需求:

  • 在发送请求之前,先判断用户是否有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的情况下:

    1. 进行请求拦截器,调用了登陆方法:

      在这里插入图片描述
    2. 执行原来的操作,得到结果

      在这里插入图片描述
  • 在用户有token的情况下,但是服务器已经没有维护用户的登陆状态了

    1. redis当中没有维护用户状态了

      在这里插入图片描述

    2. 原本请求–>登陆请求–>原本请求

      在这里插入图片描述

    3. 后端已经维持用户登陆状态

      在这里插入图片描述



总结:

  • 虽然这样封装看起来挺复杂的,但是对于用户来说是一种很好的体验。对于普通的网站,如果发现你你的登陆过期,又得重新输入密码注册。但是这样我们无全不需要,只要你已经是登陆过的用户,除非你清除了小程序的缓存,否则在你接下来使用过程中都不需要再手机登陆。
  • 其次,对于前端js编写来说,不需要在每个请求前都去判断用户是否本地缓存有token,都在由请求拦截器统一处理。对于请求后的响应,我们不需要去处理各种情况,只需要处理成功的情况就可以了。



关于实现小程序用户“无感”登陆的方法,我相信这不是最优解,但是这是我目前能想到最好的解决办法了。如果小伙伴有更好的登陆流程,欢迎在评论区告诉我,一起讨论~



版权声明:本文为m0_53215500原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。