微信小程序接口鉴权异步问题
前言
前段时间开始做一个新的小程序,因为某些原因,一进入小程序就需要wx.login拿code去调一次后端的登录接口拿一个token,之后几乎所有的接口都需要用到这个token作为参数去请求,这就有点类似于接口的鉴权,一开始我也确实是按照接口鉴权那种方式去做的,但在这其中发现了不少问题。
与普通接口鉴权的区别
通常情况下的接口鉴权,是有一个登录的过程的,这个登录过程是由用户自主触发的,但小程序里面的登录,由于小程序机制的问题,一进入小程序就可以通过wx.login去识别用户,是不用用户自主触发的,这一点差异带来的不同是巨大的,意味着那些需要token的接口必须要在登录接口调用完成之后再发起请求,否则就拿不到token,虽然一般token会存一下在storage里面,但是在用户首次进入小程序的时候肯定会有问题。
网上一些方法的问题
在网上查找了许多办法,大多数的解决方案是在app.js里面写一个promise登录方法,然后在页面调用,在回调里面继续调用那些需要token的请求方法。这么能够解决问题,但是相当麻烦,每个页面里面都有一个login的请求,而且在分组件的时候就不能把请求放在组件里面,页面需要鉴权的接口就必须全部放在页面的login回调里面,这是因为如果在组件里面也做需要token的接口请求,会跟页面里的login重叠,导致一个页面请求了两次登录接口。还有另外一种方法就是自己做一个请求拦截,在请求拦截里面判断一下有没有token,没有的话就调用login去拿。用这种方法就必须把页面的请求排成一个队列,不能让几个需要token的接口同时进行请求,一旦几个接口同时请求,在首次进入的时候会同时触发几个login接口去拿token,造成许多不必要的请求。
解决方案
网上的一些方法还是不靠谱,只能自己写了。最初的想法还是在接口拦截里面做,所谓的接口拦截其实就是自己根据微信的方法封装一个项目通用的请求方法,在封装的方法里面拦截。一般绝大多数项目都会这样做,类似于axios的请求拦截封装,本人项目中用的taro框架(很垃圾,最好别用),taro框架中自带的请求方法本身已经封装成promise了用起来比较方便,普通的小程序还得先把请求方法进行一下promise封装。我的办法是在第一次调用需要token的时候去请求login,如果在这个login请求进行中时,阻塞住其他需要token的接口,让其等待第一个login请求完成之后再去请求。在第一次login请求完成之后存一下storage,然后后面的请求就可以直接去storage里面拿了。
请求拦截封装
// request.js
import merge from 'merge'
import {
requestOptions } from './options'
import {
request as taroRequest } from '@tarojs/taro'
/**
* 请求参数封装
* @param {Object} options 参考https://nervjs.github.io/taro/docs/apis/network/request/request.html#docsNav
*/
export default async function request(options) {
// 用merge合并默认options
options = merge.recursive(true, requestOptions, options)
// 接口需要token,先尝试从缓存里面拿
if (options