Token expiration in the small program to re-log in and re-request the service interface

First of all, let’s look at the small program official small program login process:

  • Applets official login address
  • Applets official login flowchart

The token here refers to the official custom login state. The token is usually time-sensitive. According to this process, when the token is invalid, the server will definitely consider the token as illegal when the page requests the interface, and the small program needs to obtain a new token. Then continue with the following process. In general, the server will give a specific status code to mark the need to obtain the token again. In the following scenarios, we will use the server to return code 401 to consider the need to log in.

Scenario 1: After the token is invalid, the login page is displayed

Must because getUserInfo interface changes, the user manual to trigger a button, usually for a specific authorization login page or pop-up window, that at the moment when you return to 401 at the interface that we just jump to the login page or pop-up authorized popups, rather then go users for the first time into the process of the small program.

This solution is the simplest solution, but its disadvantages are obvious: it interrupts the user’s operation process and repeats the process of the first authorized login, which is very complicated.

Scenario 2: No token is sensed after the token expires and the previous operations continue

There are two steps to solve this problem: obtain token without perception

Call wx.login() to get the code and then call the new interface on the server side. This interface only needs to accept the code and return the latest information of the current login account (token and other information) to get the latest token.

The specific code is as follows:

HTTP. Js file core code

export class Http {
  constructor() {}

  request({ url, data = {}, method, header, callback = ' '} = {}) {
    let _this = this;
    return new Promise((resolve, reject) = > {
      wx.request({
        url,
        data,
        method,
        header: { Authorization: 'Bearer ' + storge.get(LOGIN_TOKEN) },
        callback,
        fail(res) {
          reject(res)
        },
        complete: res= > {
          if (callback) return callback(res.data);
          let statusCode = res.statusCode;
          if (statusCode == 404) {
            console.log('Interface does not exist')}else if (statusCode == 401) {
           getNewToken().then(() = > {
              _this.request({ url, data, method, callback: resolve })
           })
          } else if (statusCode == 200) {
            resolve(res.data)
          } else if (statusCode.startsWith('5')) {
            wx.showModal({ content: 'Server error, please try again! '.showCancel: false}); }}})})}}/ / access token
const getNewToken = () = > {
  return new Promise((resolve, reject) = > {
    wx.login({
      success(res) {
        wx.request({
          url: 'Get the latest token interface address'.method: 'POST'.success(res) {
            let r = res.data;
            // Re-execute all requests stored in the observer array.
            if (r.code == 0) {
              const token = r['data'] ['token'];
              wx.setStorageSyn('LOGIN_TOKEN', token); resolve(res); }}})},fail(err) {
        reject()
        console.error('wx login fail', err); }}); })}Copy the code

Here’s how to use it:

API /index.js file part OF the API

import { Http } from '.. /utils/http.js';
export class Index extends Http {
  constructor() {
    super(a); }// Get the category
  goodsList(data) {
    return this.request({
      url: 'goodsList'.method: 'GET'.data: data }); }}Copy the code

Pages /index/index.js file to invoke this interface, part of the code:

import { Index } from '.. /.. /api/index.js';

const API = new Index();

Page({

  /** * initial data for the page */
  data: {},
  
  // Get the list of items
  getGoodList(page) {
    let param = this.data.params;
    let params = { page: page, ... param };return new Promise(resolve= > {
      API.getGoodList(params).then(res= > {
        // Business logic})})}... })Copy the code

So, let’s try it out and see if it works

As shown in the figure, when the user interface is switched, the token is found invalid (the server side returns 401), and the user interface is switched to issue to obtain the latest token, and then the user interface is continued. The user is unaware of the whole process, and the previous operation is not interrupted, is it perfect? To look down

When you encounter a page with complex business, there will be multiple requests. In this case, the interface to obtain the latest token will be called several times. In fact, this interface only needs to be called once, which will affect the performance. In fact, there are many ways, the simplest is to use the publish and subscribe model, simple modify the code as follows:

let isRefreshing = true;
let subscribers = [];

function onAccessTokenFetched() {
  subscribers.forEach((callback) = > {
    callback();
  })
  subscribers = [];
}

function addSubscriber(callback) {
  subscribers.push(callback)
}

export class Http {
  constructor() {}

  request({ url, data = {}, method, header, callback = ' '} = {}) {
    let _this = this;
    return new Promise((resolve, reject) = > {
      wx.request({
        url,
        data,
        method,
        header: { Authorization: 'Bearer ' + storge.get(LOGIN_TOKEN) },
        callback,
        fail(res) {
          reject(res)
        },
        complete: res= > {
          if (callback) return callback(res.data);
          let statusCode = res.statusCode;
          if (statusCode == 404) {
            console.log('Interface does not exist')}else if (statusCode == 401) {
            
            // Cache the interface that needs to be re-executed in a queue
           addSubscriber(() = > {
             _this.request({ url, data, method, header, callback: resolve })
           })
            
           if (isRefreshing) {
              getNewToken(url, data).then(() = > {
                // Execute the cache interface in turn
                onAccessTokenFetched();
                isRefreshing = true;
              })
            }
            isRefreshing = false; 
          } else if (statusCode == 200) {
            resolve(res.data)
          } else if (statusCode.startsWith('5')) {
            wx.showModal({ content: 'Server error, please try again! '.showCancel: false}); }}})})}}/ / access token
const getNewToken = () = > {
  return new Promise((resolve, reject) = > {
    wx.login({
      success(res) {
        wx.request({
          url: 'Get the latest token interface address'.method: 'POST'.success(res) {
            let r = res.data;
            // Re-execute all requests stored in the observer array.
            if (r.code == 0) {
              const token = r['data'] ['token'];
              wx.setStorageSyn('LOGIN_TOKEN', token); resolve(res); }}})},fail(err) {
        reject()
        console.error('wx login fail', err); }}); })}Copy the code

Yes, that’s so short answer, all the 401 interface to execute the cache to a queue and get the latest token (here using the lock request, only perform a access token interface), then, in turn, to go before the logic, the whole process without awareness, and page regardless of how many requests, always only a token for interface. Here’s a simple test:

It can be seen that the issue interface is called only once, so that it can perfectly solve the small program in which the token expires and re-log without awareness and request all the interfaces before.