The ideas in this article come from the refactoring summary of the actual project, welcome to correct and exchange. If it’s helpful for you, please support it at thumb up 👍.

Recently I refactored an old project and found that the interceptor for handling requests was rather messy. So I refactored the request processing layer of the entire project and it is now working fine in the project.

In this paper, I will share with you my thoughts on refactoring and subsequent optimization. In order to facilitate sharing with you, I use VUE3 to implement a simple demo with the same ideas. If you are interested in it, you can check it on my Github.

In this article, I will share the following points with you:

  1. Problem analysis and program design;
  2. Effect after reconstruction;
  3. Development process;
  4. Late optimization point;

If you’re not sure what an HTTP request and response blocker is, take a look at what 77.9K Star’s Axios project has to offer.

1. Demand thinking and scheme design

1. Problem analysis

At present, the old project has been developed by several colleagues. The interceptor has the following problems:

  • The code is messy and poor in readability.
  • Each interceptor’s responsibilities are chaotic and interdependent;
  • Logically problematic;
  • Different projects within the team cannot be reused;

2. Scheme design

After analyzing the above problems, my preliminary scheme is as follows: refer to the plug-in architecture design, separate each interceptor into a separate file for maintenance, achieve a single responsibility, and then schedule and register it through the interceptor scheduler.

The interceptor scheduling process is shown as follows:

Second, the effect after reconstruction

The code is actually relatively simple, here is the final implementation of the effect:

1. Directory hierarchy is clearer

The directory hierarchy of the request processing layer is clearer after the refactoring, which is roughly as follows:

2. Interceptor development is easier

To develop new interceptors in subsequent business, there are only three steps to complete the development and use of interceptors. The interceptor scheduler will automatically invoke all interceptors:

3. Each interceptor has a single role and can be pluggable

Each interceptor is separated into a single file to implement, so that each interceptor’s responsibility is separate and single, and when it is not needed to use an interceptor, it can be replaced at any time, and it can be inserted flexibly.

III. Development process

Here, I take this demo project separately as an example to introduce.

1. Initialize directory structure

Following the previous design scenario, you first need to create a directory structure in your project:

-request-index.js // Interceptor scheduler -interceptors-request // This is used to hold each interceptor -index.js // Managing all interceptors. -index.js // Manage all response interceptors and sort them

2. Define interceptor scheduler

Since our project uses the Axios request library, we need to know how to use the Axios interceptor. Here’s a quick look at the Axios documentation on how to use the interceptor:

/ / add request interceptor axios. Interceptors. Request. Use (function (config) {/ / business logic return config. }, function (error) {// business logic return promise.reject (error); }); / / add the response interceptor axios. Interceptors. Response. Use (function (response) {/ / business logic return response; }, function (error) {// business logic return promise.reject (error); });

As we can see from the above code, when using an interceptor, we simply call the corresponding method on the Axios. Interceptors object, so we can extract this piece of logic:

// src/request/interceptors/index.js import { log } from '.. /log'; import request from './request/index'; import response from './response/index'; export const runInterceptors = instance => { log('[runInterceptors]', instance); if(! instance) return; / / set the request for interceptor (const key in the request) {instance. Interceptors. Request. Use (config = > request [key] (config)); } / / set the response interceptor for (const key in response) {instance. Interceptors. Response. Use (result = > response [key] (result)); } return instance; }

This is our core interceptor scheduler. The current implementation imports all request interceptors and response interceptors, registers all interceptors through a for loop, and returns the entire Axios instance.

3. Define simple request interceptors and response interceptors

For a brief demonstration, create the following two interceptors:

  1. Request interceptor: SetLoading, which displays a global Toast box that says “Loading…” before initiating the request. Copywriting.
  2. Response interceptor: setLoading, which closes the Toast box on the page after the request is responded.

In order to unify the development specification, we agree on the plug-in development specification as follows:

/* const interceptorName = options => {log("[interceptor.request]interceptorName:", options); // Interceptor business return options; }; export default interceptorName;

First create the file SRC/request/interceptors/request/directory to create setLoading js file, plug-in development standard, according to the above agreement that we complete the following plugins:

// src/request/interceptors/request/setLoading.js import { Toast } from 'vant'; import { log } from ".. /.. /log"; */ const setLoading = options => {log("[interceptor.request]setLoading:", options); Toast.Loading ({duration: 0, message: 'Loading... ', forbidClick: true, }); return options; }; export default setLoading;

Then export the interceptor as an array so that the interceptor scheduler can register it uniformly:

// src/request/interceptors/request/index.js

import setLoading from './setLoading';

export default [
    setLoading
];

In the same way, we develop the response interceptor:

// src/request/interceptors/response/setLoading.js import { Toast } from 'vant'; import { log } from ".. /.. /log"; */ const setLoading = result => {log("[interceptor.response]setLoading:", result); If (result && result.success){toast.clear (); } return result; }; export default setLoading;

Export the response interceptor:

// src/request/interceptors/response/index.js

import setLoading from './setLoading';
export default [
    setLoading
];

4. Set the Axios interceptor globally

Following the same steps, I wrote a few more interceptors: request interceptor:

  • SetSecurityInformation. Js: add security to the requested url parameter;
  • SetSignature. Js: Add signature information to the request header of the request;
  • SetToken.js: Adds token information to the request header of the request;

Response interceptor:

  • Seteror.js: Handles errors in response results, such as closing all Toast boxes;
  • Setinvalid.js: Handles logon invalidation of the response result, such as jumping to the logon page;
  • SetResult.js: Handles the problem of data nesting too deep in response results, which willresult.data.data.dataThis type of return result is processed intoresult.dataFormat;

As for how to achieve it, you can check it on my Github if you are interested.

Then we can double encapsulate Axios and export the Request object for business use:

// src/request/index.js

import axios from 'axios';
import { runInterceptors } from './interceptors/index';
export const requestConfig = { timeout: 10000 };

let request = axios.create(requestConfig);
request = runInterceptors(request);

export default request;

We’re done over here.

If you need to make a request in your business, you can use this:

<template> <div><button @click="send"> </button></div> </template> <script setup> import request from '. /request/index.js'; const send = async () => { const result = await request({ url: 'https://httpbin.org/headers', method: 'get' }) } </script>

Test it

If we send a request, we can see that all interceptors are executed as follows:

Look at the request header:

You can see that the request interceptor we developed is in effect.

IV. TARO

Since the taro.request method is already provided in Taro as the request method, we don’t need to use Axios to make the request.

Based on the above code, it is also very simple, only need to change two places:

1. Modify the method that encapsulates the request

This is done by replacing Axios with the taro. request method and using the AddInterceptor method to import the interceptor:

// src/request/index.js import Taro from "@tarojs/taro"; import { runInterceptors } from './interceptors/index'; Taro.addInterceptor(runInterceptors); export const request = Taro.request; export const requestTask = Taro.RequestTask; Export const addInterceptor = taro.addInterceptor; // See if it is needed

2. Modify interceptor scheduler

Because Axios and Taro. Request add interceptors differently, they need to be replaced as well:

import request from './interceptors/request'; import response from './interceptors/response'; export const interceptor = { request, response }; Export const getInterceptor = (chain = {}) = bb0 {let requestParams = chain.requestParams; for (const key in request) { requestParams = request[key](requestParams); } // Set response interceptor let responseObject = chain.proceed(requestParams); for (const key in response) { responseObject = responseObject.then(res => response[key](res)); } return responseObject; };

The specific API can be seen in the Taro. Request documentation, but I won’t cover it here.

V. Project summary and thinking

This reconfiguration is mainly based on the existing business, so even after the reconfiguration of the request layer, there are still many points that can be optimized. Now I think of these as one of my Todo lists:

1. Separate the request layer into a library

Since the company now has many projects in independent sites, considering the unified development specification of the project, it can be considered to maintain the request layer as a private library independently. Current thoughts:

  • With reference to the plug-in architecture design, LERNA is used to manage all interceptors;
  • Upgrading TypeScript for easier administration and development
  • Engineering, add build tools, unit tests, UMD, etc.
  • Use documentation and develop documentation.

2. Support replaceable request library

The reason for this is that the request libraries used by our front-end team are more and more scattered. Therefore, in consideration of universality, we need to add the method to support the interchangeable request library. Current thoughts:

  • The request library adaptation layer is abstracted in the existing request layer and the unified interface is defined.
  • Built-in adaptations to several common request libraries.

3. Development of interceptor scaffolding

The purpose of this is simply to allow others on the team to use the scaffolding tool directly, quickly create an interceptor based on the built-in scaffolding template, and then develop it later, largely unifying the interceptor development specification. Current thoughts:

  • There are two built-in interceptor templates: request interceptor and response interceptor;
  • Scaffolding development is relatively simple, and parameters (such as language) are determined according to business needs.

4. Enhanced interceptor scheduling

The current implementation of this feature is relatively simple, but we still need to consider enhancing interceptor scheduling. Current thoughts:

  • Handling interceptor failure cases
  • Addressing the scheduling sequence of interceptors;
  • Interceptors execute synchronously, asynchronously, concurrently, looping, and so on;
  • Pluggable interceptor scheduling;
  • Consider referring to the Tapable plug-in mechanism;

VI. Summary of this paper

In this paper, a request-layer interceptor scheduling scheme is summarized through a simple project reconstruction, which aims to achieve a single responsibility for all interceptors, easy maintenance, unified maintenance and automatic scheduling, and greatly reduce the difficulty of interceptor development in practical business.

Subsequent where I still has a lot to optimize, as a TODO LIST, if it is made completely general, the localization may prefer to interceptor scheduling container, only provide some generic interceptor, the rest is defined by the developer library is responsible for scheduling, but on the request of the library is commonly, so do value needs to be weighed.

Of course, it is still a priority to develop and use it as a private library within the team, because basically the business used within the team is the same, just the project is different.