Meaning:

  1. Cancel repeated requests: the same interface automatically cancellations the next request when the previous pending state is pending
  2. Automatic retry: If the background interface requests are abnormal, the system automatically resends the requests for several times until the number of requests reaches the threshold
  3. Request interface data cache: The interface does not obtain data from the background within the specified time, but directly obtains data from the local cache

This article focuses on scenarios where AXIos is used as an HTTP request, using the Axios interceptor.

The Interceptors in Axios are similar to the Onion model in that they are write-first for requests and write-first for responses

It was intended to be encapsulated into multiple interceptors for use, but because the interceptors are equivalent to pipeline operations, influence and interference, and there is a conflict in the part of the logic to cancel repeated requests and request caching, it is written directly in one interceptor.

Call way

The parameter is passed on demand, that is, the function is set on each interface according to the specific situation of the interface. The setting fields are as follows:

CancelRequest: true // If this parameter is specified on the interface, the repeated request function is enabled. Retry: 3, retryDelay: 1000 // Retry number of request retries. RetryDelay Interval between retries Cache: True, setExpireTime: 30000 // Cache: true Enable the current interface cache. SetExpireTime Specifies the cache duration of the current interfaceCopy the code

Usage scenarios

It can be used in all scenarios used by Axios, including Nodejs servers and clients such as Vue, where this case is currently based on the Vue front-end

Case code

The request interface calls api.js

import request from './index'; Export default {middleViewData: data => request.get('/jscApi/middleViewData', {data}), cancelReq: data => request.get('http://localhost:3003/jscApi/middleViewData', { data, cancelRequest: ReqAgainSend: data => request. Get ('/equ/equTypeList11', {data, retry: 3, retryDelay: CacheEquList: data => request. Get ('/equ/equList', {data, cache: true, setExpireTime: '/equ/equList', {data, cache: true, setExpireTime: CacheEquListParams: data => request. Get ('/equ/equList', {data, cache: True}) // Test cache request parameters with different values};Copy the code

Axios instantiation

index.js

import { clearToken, getToken } from '@/tools/cookiesStorage.js'; // Import vuex import Axios from 'Axios '; Import {addPendingRequest, removePendingRequest} from '.. /tools/encapsulationAxios/cancelRepeatRquest'; Import {againRequest} from '.. /tools/encapsulationAxios/requestAgainSend'; Import {requestInterceptor as cacheReqInterceptor, responseInterceptor as cacheResInterceptor } from '@/tools/encapsulationAxios/requestCache.js'; import { Notification } from 'element-ui'; {code: XXX, data: XXX, MSG :'err message'} const responseHandle = {200: response => { return response.data.data; }, 401: response => {Notification({title: 'authentication exception ', message:' Login status expired, please log in again! ', type: 'error' }); clearToken(); window.location.href = window.location.origin; }, default: response => {Notification({title: 'operation failed ', message: response.data. MSG, type: 'error'}); return Promise.reject(response); }}; const axios = Axios.create({ baseURL: process.env.VUE_APP_BASEURL || '', timeout: 50000 }); / / add request interceptor axios. Interceptors. Request. Use (function (config) {/ / request header for interface token authentication getToken && () (config.headers['Authorization'] = getToken()); If (config) method) toLocaleLowerCase () = = = 'post' | | config. The method. The toLocaleLowerCase () = = = 'put') {/ / unified processing parameters, All requests use the data pass parameter config.data = config.data.data; } else if (config) method) toLocaleLowerCase () = = = 'get' | | config. The method. The toLocaleLowerCase () = = = 'delete') {/ / parameter unified handling config.params = config.data; } else {alert(' disallowed request method: '+ config.method); } addPendingRequest(config) addPendingRequest(config); // Add the current request information to the pendingRequest object // Request the cache cacheReqInterceptor(config, axios); return config; }, function(error) { return Promise.reject(error); }); / / add the response interceptor axios. Interceptors. Response. Use (response = > {/ / response is normal when removed from the pendingRequest object request removePendingRequest(response); cacheResInterceptor(response); return responseHandle[response.data.code || 'default'](response); }, the error = > {/ / removed from the list of pending requests removePendingRequest (error. The config | | {}); // If (! Axios.iscancel (error)) {againRequest(error, Axios); } / / request cache handling the if (Axios. IsCancel (error) && error. The message. The data & error. The message. The data. The config. The cache) {return Promise.resolve(error.message.data.data.data); } return promise.reject (error); }); export default axios;Copy the code

Cancelrepeatrquest.js: cancelrepeatrQuest.js:

// Cancel the repeated request /* If the user clicks the button repeatedly and successively submits two identical requests (considering the request path, method and parameters), we can choose one of the following interception schemes: 1. Cancel request A and only send request B (this will cause request A to have already been sent and be processed by the backend) 2. 3. Cancel request B, only send request A, and take the returned result of request A received as the returned result of request B. The third scheme needs to do listening processing, which adds complexity. Send only the first request. When A request is pending, all subsequent requests that are repeated with A are cancelled, and only A request is actually issued. The interception of this request is not stopped until the end of A request (success/failure). */ import Axios from 'axios'; import { generateReqKey } from './commonFuns'; // addPendingRequest: adds the current request information to the pendingRequest object; const pendingRequest = new Map(); // The Map object holds key and value pairs. Any value (object or raw value) can be used as a key or a value. export function addPendingRequest(config) { if (config.cancelRequest) { const requestKey = generateReqKey(config); If (PendingRequest.has (requestKey)) {config.cancelToken = new Axios.cancelToken (cancel => {// The parameter of the cancel function will be the promise The error is caught cancel(' ${config.url} request has been cancelled '); }); } else { config.cancelToken = config.cancelToken || new Axios.CancelToken(cancel => { pendingRequest.set(requestKey, cancel); }); } // removePendingRequest: Check whether there is a duplicate request. If there is, cancel the request. export function removePendingRequest(response) { if (response && response.config && response.config.cancelRequest) { const requestKey = generateReqKey(response.config); If (pendingRequest.has(requestKey)) {const cancelToken = pendingRequest.get(requestKey); cancelToken(requestKey); pendingRequest.delete(requestKey); }}}Copy the code

Requestagainsend.js:

Import {isJsonStr} from './commonFuns'; /** * @param {error message} err * @param {instantiated singleton} axios * @returns */ export function againRequest(err, axios) { let config = err.config; // config.retry the configured number of retransmissions on the interface if (! config || ! config.retry) return Promise.reject(err); / / set config variables used to record a retry count default to 0. __retryCount = config. __retryCount | | 0; If (config.__retryCount >= config.retry) {return promise.reject (err); } // Retry times config.__retryCount += 1; Var backoff = new Promise(function(resolve) {setTimeout(function() {resolve(); }, config.retryDelay || 1000); }); Return backoff.then(function() {// Check whether it is a JSON string // TODO: If (config.data && isJsonStr(config.data)) {config.data = json.parse (config.data); } return axios(config); }); }Copy the code

Request the cache requestCache.js

import Axios from 'axios'; import { generateReqKey } from './commonFuns'; Const options = {storage: true, // whether to enable loclastorage cache storageKey: 'apiCache', storage_expire: 600000, // localStorage data storage time 10min (refresh the page to determine whether to clear) expire: 20000 // data cache ms for each interface}; / / initialization (function () {let cache = window. The localStorage. The getItem (options. StorageKey); if (cache) { let { storageExpire } = JSON.parse(cache); If (storageExpire && getNowTime() -Storageexpire < options.storage_expire) {return; } } window.localStorage.setItem(options.storageKey, JSON.stringify({ data: {}, storageExpire: getNowTime() })); }) (); function getCacheItem(key) { let cache = window.localStorage.getItem(options.storageKey); let { data, storageExpire } = JSON.parse(cache); return (data && data[key]) || null; } function setCacheItem(key, value) { let cache = window.localStorage.getItem(options.storageKey); let { data, storageExpire } = JSON.parse(cache); data[key] = value; window.localStorage.setItem(options.storageKey, JSON.stringify({ data, storageExpire })); } let _CACHES = {}; Let cacheHandler = {get: function(target, key) {let value = target[key]; Console. log(' ${key} read ', value); if (options.storage && ! value) { value = getCacheItem(key); } return value; }, set: function(target, key, value) {console.log(' ${key} is set to ${value} '); target[key] = value; if (options.storage) { setCacheItem(key, value); } return true; }}; let CACHES = new Proxy(_CACHES, cacheHandler); export function requestInterceptor(config, If (config.cache) {let data = CACHES[' ${generateReqKey(config)} ']; // This is used to store the default time or the time passed by the user. config.setExpireTime ? (setExpireTime = config.setExpireTime) : (setExpireTime = options.expire); If (data && getNowTime() -data.expire < setExpireTime) {config.cancelToken = new CancelToken(cancel => {// The argument to the cancel function is caught as an error of the promise cancel(data); }); }}} export function Interceptor(response) {// Return code === 200 if (response &&) response.config.cache && response.data.code === 200) { let data = { expire: getNowTime(), data: response }; CACHES[`${generateReqKey(response.config)}`] = data; Function getNowTime() {return new Date().gettime (); }Copy the code

The public function commonFuns.js

import Qs from 'qs'; // generateReqKey: is used to generate the request Key based on the current request information; Export function generateReqKey(config) {// in response, data in response.config is a JSON string, If (config && config.data && isJsonStr(config.data)) {config.data = json.parse (config.data); } const { method, url, params, data } = config; Return [method, URL, qs.stringify (params), qs.stringify (data)]. Join ('&'); Export let isJsonStr = STR => {if (typeof STR == 'string') {try {var obj = JSON.parse(str); if (typeof obj == 'object' && obj) { return true; } else { return false; }} catch (e) {console.log('error: '+ STR + '!! ' + e); return false; }}};Copy the code

Today’s share is over, also welcome to pay attention to the public number front sail to communicate and see more front articles, welcome to put forward valuable suggestions!