Use axios interceptor to solve [front-end concurrency conflict]

 

preface

Some interfaces in the project will be slow when calling the third party, and repeated requests will occur in a short time

There are also users who submit data many times in a short time

There will be concurrency conflicts, which can be intercepted at the front end

At present, I have summarized two methods:

The first scheme: add loading waiting on the submit confirmation button

The second scheme: repeat submission and interception in the axios request for a unified interface

The following describes how to use the two cases

 

Add loading wait on the submit confirmation button

This method is relatively simple. It mainly refers to the usual coding habits When a vue page requests an interface, it adds loading:true before the request. After the interface call is successful, it will load: false. This is also a relatively simple and rough solution. It can be described as a white cat and a black cat. Catching a mouse is a good cat

Practical application in the project:

axios requests to add unified interception

There are many cases of concurrency in the project. When our project achieves a certain degree, it is obviously not the best solution to solve the concurrency problem by adding loading above

Every request of the interface will be concurrent. If axios in our project is uniformly encapsulated for the second time, we can add request interception at the place of unified request and automatically process repeated requests, which also greatly optimizes our business code

When axios sends http requests, the official has two core API s for intercepting requests:

1.interceptors   

n. Interceptor ship, [aviation] [military] interceptor; Interceptor; Interceptor (plural of interceptor)

There are two kinds of interceptors (request interception and corresponding interception). We can handle them in these two places

Practical application in the project:

 

2.cancel token:

Call the cancel token API to cancel the request. The official website provides two ways to build a # cancel token. We use this method: create a # cancel token by passing a # executor # function to the constructor of # CancelToken, so that duplicate requests can be detected in the above request interceptor and executed immediately:

let cancel
config.cancelToken = new axios.CancelToken(function (c) {
   cancel = c
})

All codes of the request

import axios from 'axios'
import { getToken } from '_utils/token'

export default class httpRequest {
  constructor (baseUrl = '') {
    this.baseUrl = baseUrl
    // Storage request queue
    this.queue = []
  }

  getInsideConfig () {
    const config = {
      baseURL: this.baseUrl,
      // Request timeout
       timeout: 10 * 1000,
      headers: {
        'Content-Type': 'application/json; charset=utf-8',
        'X-URL-PATH': location.pathname
      },
    }
    return config
  }

  // Make configuration modification for special configuration requirements - you can add configuration for special interfaces
  changeConfig (options) {
    let tempOptions = options
    return tempOptions
  }

  // Destroy request instance
  destroy (config) {
      let matchIndex = findMatchIndex(this.queue, config)
      if (matchIndex !== -1) {
        this.queue.splice(matchIndex, 1)  //After the interface request succeeds, it is deleted from the array of the request queue
      }
    LoadingBar.finish()
    store.dict.state.loading = false
  }

  // Intercept request
  interceptors (instance, url, method) {
    // Add request interceptor
    instance.interceptors.request.use(
      config => {
        if (config.isLoading) {
          // Here you can add global loading You can add the state of the interface to the store for global use
        }

        // Process duplicate requests
          let cancel
          config.cancelToken = new axios.CancelToken(function (c) {
            cancel = c
          })

          let matchIndex = findMatchIndex(this.queue, config)
          if (matchIndex !== -1) {
            Notice.error({
              title: 'Tips',
              desc: 'Please do not submit again'
            })
            cancel('Request interrupt')
          } else {

            //When it is judged that the interface does not have repeated requests, the interface is added to the request queue
            this.queue.push({
              url: config.url,
              method: config.method,
              params: config.params
            })
          }
     
        // Processing before sending a request
        config.headers.authorization = getToken()
        return config
      },
      error => {
        // Request error handling
        return Promise.reject(error)
      }
    )

    // Add response interceptor
    instance.interceptors.response.use(
      res => {
        let { config } = res
        if (config.isLoading) {
          // Destroy request instance after request response
          this.destroy(config)
        }
        let { data } = res
        // code verification
        let { code } = data
        let { state } = data
        if (code === 200) {
          // No permission
          if (code === 'code_400') {
         
            return Promise.reject(data)
          }
          // Error 401 invalid token delete token return to authentication center (login page)
          if (code === 401) {
            logoff()
            // Other error handling
          }
          if (code === 'code_500') {

            return Promise.reject(data)
          }
          if (code === 'code_406') { //Incorrect request parameters!
      
            return Promise.reject(data)
          }
        }
        if (state === '500') { //Server error
         
        }
        return data
      },
      error => {
        LoadingBar.error()
        // Response error handling
     
        return Promise.reject(error)
      }
    )
  }

  request (options) {
    const instance = axios.create()
    // Merge options
    options = this.changeConfig(Object.assign(this.getInsideConfig(), options))
    // Register interceptor
    this.interceptors(instance, options.url, options.method)
    // Return instance
    return instance(options)
  }
}

function findMatchIndex (map, config) {
  return map.findIndex(item => {
    if (
      config.url.includes(item.url) &&
      item.method === config.method &&
      diff(item.params, config.params)
    ) {
    }
    return (
      config.url.includes(item.url) &&
      item.method === config.method &&
      diff(item.params, config.params)
    )
  })
}

function diff (obj1, obj2) {
  if (Object.keys(obj1).length !== Object.keys(obj2).length) {
    return false
  }
  let obj1Keys = Object.keys(obj1)
  for (let i = 0; i < obj1Keys.length; i++) {
    if (obj1[obj1Keys[i]] !== obj2[obj1Keys[i]]) {
      return false
    }
  }
  return true
}

summary

Concurrent requests are quite common. Interface requests are processed uniformly. However, in a specific business, the interface that takes a long time to request an interface still needs to control the button when submitting data through the global loading state. Loading or disabling is added to the button, which is treated according to its own business

 

Keywords: Front-end Vue

Added by easyedy on Wed, 02 Feb 2022 06:13:28 +0200