sunny2018
sunny2018
  • 发布:2019-12-04 12:33
  • 更新:2020-05-07 18:52
  • 阅读:9689

分享关于uniapp和后台信息传输,token会话,uni.request封装的一些方法技巧。

分类:uni-app

提前背景:
我是主要做后台开发的,用的是thinkphp3.2.最近需要开发小程序。前台数据调用需要我写。在开发的过种中遇到如下问题:
1,如何在每次数据调用中在uni.request中带上用户登录信息。或者说有没有统一的方法。不用每个地方请求都要写上一些登录信息。这样看起来非常不好,使用也不爽;
2,前后端会话的token,如何规定。
3,整体项目中如何针对全局的出错做统一判断,如登录信息不存在。token失效等。

带着上面这些问题,专研了两天。最后分享一些我的解决方案;
问题1:最后使用了封装方法。即我把uni.request放到自己写的方法里,然后所有ajax请求都用自己写的封闭方法:

//接口地址前缀  
Vue.prototype.serverUrl="http://xxx.com/index.php/Home";  
//封装uni.request方法  
Vue.prototype.uni_request=function(params){  
    /*默认值*/  
    params.showLoading=params.showLoading==undefined?true:params.showLoading;  
    params.method=params.method==undefined?"GET":params.method;    
    params.header=params.header==undefined?{'X-Requested-With':'XMLHttpRequest'}:params.header;    
    //请求头里一定要带上用户登录信息,没得商量  
    if(params.header.memberInfo==undefined||params.header.memberInfo==""){  
        params.header.memberInfo=JSON.stringify({"member_id":uni.getStorageSync("memberInfo").member_id,"token":uni.getStorageSync("memberInfo").token});         
    }  
    /**/  
    if(params.showLoading){  
        uni.showLoading({mask:true,title:(params.showLoadingTitle?params.showLoadingTitle:"加载中...")});  
    }  
    uni.request({  
        url: this.serverUrl+params.url,  
        data:params.data,  
        header:params.header,             
        method:params.method,             
        success: (res) => {  
            var status=res.data.status;  
            var data=res.data.info;   
            var loginStatus=res.header.loginStatus;  
            var newToken=res.header.newToken;  
            //登录状态:登录异常  
            if(loginStatus==0||loginStatus==""||loginStatus==undefined){  
                uni.showModal({  
                    title: data,  
                    content: '',  
                    success:function(){  
                        uni.navigateTo({url:"../Registered/Registered"});  
                    }  
                });  
                return;  
            }  
            //更新token  
            if(newToken){  
                var memberInfo=uni.getStorageSync("memberInfo");  
                memberInfo.token=newToken;  
                uni.setStorageSync("memberInfo",memberInfo);  
            }             
            //服务端返回错误  
            if(status!=1){                
                if(params.fail){  
                    params.fail(res.data);  
                }else{  
                    uni.showToast({  
                        title: data,  
                        duration: 3000,  
                        "icon":"none"   
                    });  
                    return false;  
                }  
            }else{  
                if(params.success){  
                    params.success(data);  
                }  
            }  
        },  
        complete: () => {  
            if(params.showLoading){  
                uni.hideLoading();  
            }  
        }  
    });   
} 

以上代码放到main.js中
封装的方法调用:
比如我在首页调用banner:

this.uni_request({  
    "url":'/Ad/index/?adid=7',  
    success: (data) => {  
        this.bannerData=data.data;  
    },                                    
});

现在我对代码分析讲解一下
1,方法传参按照uni.request的结构来的,传参也是用对象方式来传递的。
接口参数说明:

2,params.showLoading这个是控制加载动画的,默认开启的。

3,params.header.memberInfo(C端向S端发起header头)
这个是C端向S端传递用户的登录参数,包含:member_id,token。
后台我是用thinkphp3.2框架,我把用户检测的方法放到了一个基本类里,整个项目都继承这个类(除了注册,登录类和其它不需要验证用户登录的类)。

4,S端返回的数据
var status=res.data.status;//业务逻辑判断,比如数据不合法,必填字段验证等
var data=res.data.info;//数据,当status和loginStatus为0的时候,返回的是出错的提示内容(Sting)
var loginStatus=res.header.loginStatus;//是获取后面传递过来的登录状态。1为正常,0未异常;
var newToken=res.header.newToken;//S端每隔一定时间会生成新和token,这个时候C端要更新。这里会有一个问题,就是当并发请求的时候会有时间差,等下会在下面讲到;
5,登录状态判断

            //登录状态:登录异常  
            if(loginStatus==0||loginStatus==""||loginStatus==undefined){  
                uni.showModal({  
                    title: data,  
                    content: '',  
                    success:function(){  
                        uni.navigateTo({url:"../Registered/Registered"});  
                    }  
                });  
                return;  
            }

6,更新token

            //更新token  
            if(newToken){  
                var memberInfo=uni.getStorageSync("memberInfo");  
                memberInfo.token=newToken;  
                uni.setStorageSync("memberInfo",memberInfo);  
            }   

这里要说一下上面提到的并数据并发的时候会有token过期的问题,场景如下:
A在10点登录平台了(这里系统生成新的tokenA)
然后用户在10:31点击某个面页,这个页面有两个ajax请求,是并发的。比如第一个ajax请求banner图,第二个ajax请求数据列表
那么第一个ajax请求带的是tokenA。同时服务器接收到tokenA后,会生成新的tokenB(因为已经过30分钟了)。
同时第二个ajax请求也到达了(这时候带的token还是tokenA,因为是并发请求的,那时候还没有tokenB产生呢),那第二个ajax就会报错了,提示token不对
显示出现的问题大家也知道了,我在解决方案如下:
一个token过渡,即在一定时间(一般5分钟)内,tokenA,tokenB都可以用,即生成新的token,老的token要缓存一下。参考微信的access_token原理。

7,逻辑判断处理

            //服务端返回错误  
            if(status!=1){                
                if(params.fail){  
                    params.fail(res.data);  
                }else{  
                    uni.showToast({  
                        title: data,  
                        duration: 3000,  
                        "icon":"none"   
                    });  
                    return false;  
                }  
            }else{  
                if(params.success){  
                    params.success(data);  
                }  
            }

当status字段(业务逻辑判断字段,上面讲到了)不为0的时候如果用户定义了fail方法则调用用户的fail方法,没有定义fail则弹出提示框,并返回。
success也是。但success没有默认的处理机制,因为方法不知道你调用成功了要干什么啊,所以一般success一般都要定义的。
注意:
上面的success和fail方法是在业务逻辑判断后所用的回调,和uni.request定义的success,fail不一样地。

以上是根据我的项目需地写的方法,欢迎大家探讨,针对我的方法不合理之处提出指导。

附件是main.js和后端的一些代码,我打包传上来了。

1 关注 分享
t***@163.com

要回复文章请先登录注册

1***@qq.com

1***@qq.com

很好,很实用
2020-05-07 18:52
sunny2018

sunny2018 (作者)

回复 丫丫amy :
错误有可能出在这下面两个方法里,这里要求返回的data为string,你看看数据格式对不对。这个是需要和后端开发人员协调的。
uni.showModal({
title: data,
content: '',
success:function(){
uni.navigateTo({url:"../Registered/Registered"});
}
});

uni.showToast({
title: data,
duration: 3000,
"icon":"none"
});
2019-12-23 18:04
sunny2018

sunny2018 (作者)

回复 丫丫amy :
你应该把报错的地方贴出来,而且方法里没有title字段啊,只有showLoadingTitle,而且这个是非必填字段。
2019-12-23 18:01
丫丫amy

丫丫amy

请教一下,将上述代码复制到main.js 后,在登录页面调用提示报错:parameter.title should be String instead of Undefined
2019-12-20 14:02
t***@163.com

t***@163.com

高手!
2019-12-12 21:25
3***@qq.com

3***@qq.com

回复 sunny2018 :
也不是所有的都调用了两次,概率性的出现。。。
坑大了。
没办法,先用原生的方法用着,等有时间了,再好好填这个坑
2019-12-12 19:46
sunny2018

sunny2018 (作者)

回复 3***@qq.com :
我是只调用了一次啊,经过测试的。为什么你会调2次呢?
2019-12-09 15:08
sunny2018

sunny2018 (作者)

回复 三段码农 :
请求拦截在hbuilder里开发过app用过,x版本没有找到。有代码麻烦贴下来分享一下。
2019-12-09 15:07
三段码农

三段码农

请求拦截了解下,最初我也是所有请求每次都锤一遍参数···后来 真香
2019-12-09 10:51
3***@qq.com

3***@qq.com

不知道为啥,该方法都调用了两次。。。
不知道作者有没有发现
2019-12-09 09:27