最近在试着搭建一个FlutterAPP的骨架,网络请求授权是其中很重要并且很基础的内容,是一个应用不可或缺的模块。
Axios 中根据后端状态码来刷新
我最开始哪家公司,我开始接手项目是没有jwt概念的,如需要用户信息就前端手动传uid参数给后端。后面后端应该是找了一个框架, 规定了token过期返回401,refToken过期返回402。
我就在axios的拦截器里面状态进行拦截这两个状态码,成功刷新好token后就把当前请求更换token后重新发起,402就直接让用户退出登录。
1 | axios.interceptors.response.use((response) => { |
这种方案个人感觉是很合理的,不过按我现在来看还是有可以优化的空间,比如同事几个请求一起过期的情况下,他这每个请求都会发起一个刷新token的请求,很是浪费。
Axios 根据过期时间来提前刷新
目前这个公司项目PC端是买的项目,移动端是我从0搭建起来的。他pc端是直接通过setInterval
, 30s去检查一下,快过期了就刷新一下。他这个方案竟然没出问题,我是想不明白。
我的实现方案是存储token是的是把过期时间减去10分钟,然后每次获取token判断下,过期就等待新token,没过期就直接返回本地的,不过我设计了一个队列锁, 把Promise对象的 resolve, reject收集存储起来,多个请求一起进入也只会发起一个刷新token请求。
Dart & Flutter中基于dio的实现
在dart里面,我最开始以为Future
应该和js的Promise
差不多的,但是还是很有区别的。
首先是一些默认的数据配置,dio支持函数式声明这些配置的。
1 | final _options = BaseOptions( |
拦截器也很简单,但是不能函数式的去挂载,必须在构造实列下面去。
1 | /// 自定义实现拦截器 |
还是和axios的思路一样,每次请求去拿token,取token是一个异步方法,先判断是否过期,过期就去刷新,没过期直接去缓存token。
还有一个比较坑的点,再拦截器的onRequset
里面,如果有异常抛出,请求不会发出去,但是异常也不会跑出去,需要手动捕获之后调用handler.reject()
,被Promise
的全自动抛出养残废了,这个问题困扰了我大半天。try{}catch(err){}
和catchError
都被我试遍了。
然后刷新token这里,翻了半天文档也没有看到Future
那种把resolve
手动收集起来,批量触发的方案,我都想写一个简单的事件总线那种来实现了,后面我无意中翻到Completer
,才发现需要这样实现。
第一次进入刷新token方法这个函数,直接加上锁,刷新完之后解开。如果上锁的时候进入刷新方法,直接返回一个Completer
实列,然后把他加到一个列表里面。等下刷新完token之后清空列表,并且调用每个completer.complete
。刷新失败也是情况列表,不过调用completer.completeError
,以后还可以加上退出登录的逻辑。
1 | /// 刷新token |
刷新token的请求建议重新一个实列来发起,如果就用标准实列, 那就是互相依赖的引用,在uniapp
项目中我已经因为这个问题首页白屏过了。