demand
Let’s say we have a form to submit, and the user clicks submit twice, generating two requests, and the backend generates two pieces of data. (Not considering backend verification)
preface
The axios (Ajax) solution for cancelling repeated requests is basically one of the following:
1. Define pending arrays to collect request information. 2. Generate the cancel function and cancelToken with AXIos. 3. Before each AXIOS request, determine whether the request information is in pending. If so, cancel the previous request using the cancel function and cancelToken generated by AXIOS, and then add the request information to pending. If no, add it directly. 4. After the interface returns, remove the request information from the Pending array.Copy the code
There are two problems with this scheme:
- If the user keeps triggering ajax requests, the interface will be cancelled until the user stops triggering. Similar to the implementation of anti-shake principle.
- The applicable scenario does not meet my requirements and cannot avoid the scenario of repeated form submission. Because when you cancel an interface, it is possible that the interface sent but the server did not respond or the server responded and did not return. There is no guarantee that the interface will be cancelled before the server responds.
In fact, this scenario works when you have a paginated table where the user switches page numbers frequently from 1 to 10. When the switch stops on page 10, the interface requesting the data on page 10 will be issued, and the interface before it will be cancelled to ensure the accurate line of data on the current page.
Relevant methods and implementation schemes are not discussed in this paper, and specific information can be consulted.Copy the code
So, are there any other solutions to implement the required scenarios
Implementation scheme
In fact, it is ok to transform the scheme mentioned just now.
Analysis of the
In AXIos, repeated form submissions are intercepted, that is, if the interface request already exists, subsequent interface requests need to be blocked from being requested again. Therefore, we also implement this in this section:
1. Define an array to record the request interface. 2. In axios, openPreventRequest is enabled. If the value is true, it indicates that the interface is enabled to intercept repeated requests. 3. If the openPreventRequest is true, the interface preventRequest checks whether the array has the same request. If yes, the request is blocked. 4. After the data is returned (whether it succeeds or fails), remove the information about the request from the array to ensure that only one array contains the same request. 5. Switch routes and reset the array.Copy the code
With that in mind, all that remains is code implementation.
Code implementation
No more nonsense, direct code scheme
- Define the preventRequest file
// Cache the requested interface information
const requestMap = []
/** * check if the request is repeated@param {Object} config* /
const checkRepeatRequest = config= > {
const requestInfo = getRequestInfo(config)
return requestMap.includes(requestInfo)
}
/** * Add request *@param {Object} config* /
const addRequest = config= > {
// Get the current request information
if(! config.openPreventRequest)return
const requestInfo = getRequestInfo(config)
requestMap.push(requestInfo)
}
/** * Remove request *@param {Object} config* /
const removeRequest = config= > {
if(! config.openPreventRequest)return
const requestInfo = getRequestInfo(config)
const requestIndex = requestMap.indexOf(requestInfo)
if (requestIndex > -1) {
requestMap.splice(requestIndex, 1)}}/** * get the request information *@param {Object} config* /
function getRequestInfo (config) {
// Repeat requests are defined as: same method, URL
// Can be self-extensible
const { method, url } = config
return [
method,
url
].join('&')}export default {
checkRepeatRequest,
addRequest,
removeRequest
}
Copy the code
- Axios configured in
// axios
import axios from 'axios'
import preventRequest from './preventRequest'
// Axios configuration...
// do something
// Encapsulate the AXIos request
function request (options) {
const{ url, method, ... other } = options// Whether to enable the interception of repeated requests
if (options.openPreventRequest) {
// Whether there is a repeat request queue
if (preventRequest.checkRepeatRequest(options)) {
return new Promise((resolve, reject) = > {
// do something
reject(new Error('cancelRequest'))
})
}
preventRequest.addRequest(options)
}
// when requested by axios
return new Promise((resolve, reject) = > {
axios.request({
method: method || 'get', url, ... other }) .then((res) = > {
resolve(res)
})
.catch(err= > {
reject(err)
})
})
}
// Axios responds to the interceptor
axios.interceptors.response.use(response= > {
preventRequest.removeRequest(response.config) // At the end of the request (HTTP code 200), remove this request
return response
}, error= > {
preventRequest.removeRequest(error.config) // If the request fails (HTTP code is not 200, timeout is cancelled, etc.), remove the request
return Promise.reject(error)
})
// Encapsulate the get method
function get(url, opt) {
return request({ url, method: 'get'. opt }) }// Encapsulate the POST method
function post(url, opt) {
return request({ url, method: 'post'. opt }) }export {
get,
post
}
Copy the code
- Turn on the switch to block repeated requests in the request file
You can configure the switch where the interface file is defined as follows, or you can configure the switch directly in the business layer as required. If you want to pass this parameter in.
// src/api/list/*.js
import { post } from '@/config/axios'
export const getSomething = body= > {
return post('/api/something', {
data: body,
// Turn on the cancel repeat request switch when requesting an interface
openPreventRequest: true})}Copy the code
- Clear the array for collection requests during route switching (vue-router as an example)
import preventRequest from '@/config/preventRequest'
export default (router) => {
router.beforeEach(async (to, from, next) => {
preventRequest.clearRequestMap()
// do something
return next()
})
}
Copy the code
This solves the problem mentioned in the requirement of intercepting duplicate form requests
Q&A
Q: What are the usage scenariosCopy the code
A: Applicable to the scenario where A form is submitted repeatedly (or where interface requests initiated by repeated submissions need to be intercepted). – Block at axiOS level to solve a bug caused by repeated form submission.
Q: How is the implementation in AXIos different from the implementation in the business layer through some instructionsCopy the code
A: From A result point of view, there is no difference. The business layer, by encapsulating certain specifications, defines a lock value, lock = true when the interface requests, lock = false when the interface returns, to intercept subsequent actions, and thus also solves the problem mentioned in the requirements. Each has advantages and disadvantages, each has flexible point and limitation point, respectively consider the use.
Q: Can openPreventRequest be enabled on the service layer?Copy the code
A: The business layer can be configured and pass-through into axios, SRC/API /list/* interface files and used in the actual business layer, there is no difference from an implementation perspective.
Q: If the AXIos timeout problem is resolvedCopy the code
A: Timeout value according to the project configuration of time to do it, before the timeout, response interceptor unified handling, timeout will trigger axios. Interceptors. Reject function in the response and then removed from the requestMap the request, Specific error events are distinguished in Reject by obtaining the function triggered by the error event via error.constructor.name.
The end of the
At this point, you have solved the problem mentioned in the initial requirements. The user has to configure the requestMap in vue-router when switching routes to clear the requestMap. Is it possible to configure it in the preventRequest file for automatic cleanup? A language point wake up, immediately to achieve, found that there is another world……
To be continued~~