The ideas in this article come from the refactoring summary of the actual project. Corrections and exchanges are welcome. If it is helpful to you, please also like it 👍Favorite to support it.
I recently refactored an old project and found that the request interceptor was written quite messy, so I refactored the request processing layer of the entire project, and it is now running normally in the project.
This article will share with you my thinking remodeling ideas and subsequent optimization, to facilitate share with you, I used Vue3 achieve a simple demo, the idea is the same, interested friends can in my Github View , this article will This demo implemented by Vue is introduced as an example.
In this article, I will mainly share the following points with you:
- Problem analysis and program design;
- Effect after reconstruction;
- development process;
- Late optimization point;
If you still don’t know what an HTTP request and response interceptor is, you can take a look at "77.9K Star’s Axios Project What are Worth Learning Places" .
1. Demand thinking and plan design
1. Problem analysis
At present, the old project has been developed by many colleagues, and the interceptor has the following problems:
- The code is messy and poorly readable;
- The responsibilities of each interceptor are chaotic and interdependent;
- There is a logical problem;
- Different projects within the team cannot be reused;
2. Scheme design
After analyzing the above problems, my preliminary plan is as follows:
Reference plug-in architecture design , independent of each interceptor , each interceptor pulled out into a separate file maintenance, do duty single , then interceptor scheduler scheduling and registration.
The interceptor scheduling process is as follows:
Second, the effect after reconstruction
The code is actually relatively simple, let's take a look at the final implementation effect:
1. The directory hierarchy is clearer
After refactoring, the directory hierarchy of the request processing layer is clearer, roughly as follows:
2. Interceptor development is more convenient
In the subsequent business development of a new interceptor, it only takes 3 steps to complete the development and use of the interceptor. The interceptor scheduler will automatically call all interceptors :
3. Each interceptor has a single responsibility and is pluggable
Extract each interceptor into a file to achieve, so that each interceptor separate responsibilities and a single . When there is no need to use a certain interceptor, it can be replaced at any time, and it can be plugged and unplugged flexibly.
Three, the development process
I am here to be alone out of the this demo project example to introduce.
1. Initialize the directory structure
According to the previously designed scheme, you first need to create a directory structure in the project:
- request
- index.js // 拦截器调度器
- interceptors
- request // 用来存放每个请求拦截器
- index.js // 管理所有请求拦截器,并做排序
- response // 用来存放每个响应拦截器
- index.js // 管理所有响应拦截器,并做排序
2. Define the interceptor scheduler
Because the project uses axios request library , we need to know how to use the axios interceptor. Here is a brief look at how to use the interceptor in the
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 业务 逻辑
return config;
}, function (error) {
// 业务 逻辑
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 业务 逻辑
return response;
}, function (error) {
// 业务逻辑
return Promise.reject(error);
});
From the above code, we can know that when using the interceptor, we only need to call 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;
// 设置请求拦截器
for (const key in request) {
instance.interceptors.request
.use(config => request[key](config));
}
// 设置响应拦截器
for (const key in response) {
instance.interceptors.response
.use(result => response[key](result));
}
return instance;
}
This is our core interceptor scheduler . Currently, after importing all request interceptors and response interceptors, it passes through the for
loop, registers all interceptors, and finally returns the entire axios instance.
3. Define a simple request interceptor and response interceptor
Here we do a simple demonstration and create the following two interceptors:
- Request interceptor: setLoading , the role is to display a global Toast box before initiating the request, prompting the "loading..." copy.
- Response interceptor: setLoading , the role is to close the Toast box in the page after the request response.
In order to unify the development specifications, we agree that the plug-in development specifications are as follows:
/*
拦截器名称:xxx
*/
const interceptorName = options => {
log("[interceptor.request]interceptorName:", options);
// 拦截器业务
return options;
};
export default interceptorName;
First create the file src/request/interceptors/request/
under the directory setLoading.js
, according to the above agreed plug-in development specifications, we complete the following plug-in development:
// src/request/interceptors/request/setLoading.js
import { Toast } from 'vant';
import { log } from "../../log";
/*
拦截器名称:全局设置请求的 loading 动画
*/
const setLoading = options => {
log("[interceptor.request]setLoading:", options);
Toast.loading({
duration: 0,
message: '加载中...',
forbidClick: true,
});
return options;
};
export default setLoading;
Then export the request interceptor, and export a array to facilitate the unified registration of the interceptor scheduler:
// src/request/interceptors/request/index.js
import setLoading from './setLoading';
export default [
setLoading
];
In the same way, we develop a response interceptor:
// src/request/interceptors/response/setLoading.js
import { Toast } from 'vant';
import { log } from "../../log";
/*
拦截器名称:关闭全局请求的 loading 动画
*/
const setLoading = result => {
log("[interceptor.response]setLoading:", result);
// example: 请求返回成功时,关闭所有 toast 框
if(result && result.success){
Toast.clear();
}
return result;
};
export default setLoading;
Export 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 as before, I wrote a few more interceptors:
Request interceptor:
- setSecurityInformation.js: Add security parameters to the requested url;
- setSignature.js: Add signature information to the request header;
- setToken.js: Add token information to the request header of the request;
Response interceptor:
- setError.js: handle the error situation of the response result, such as closing all toast boxes;
- setInvalid.js: handle the login invalidation of the response result, such as jumping to the login page;
- setResult.js: the response result of the data processing problem deeply nested, the
result.data.data.data
such results of the processing returns toresult.data
format;
As for how to achieve, we are interested can in my Github view .
Then we can re-encapsulate request
and export the 060f4f1cb737d1 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;
Finished here.
Need to initiate a request in the business, you can use it like 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>
5. Test it out
The development is almost the same here. We send a request and you can see the execution process of all interceptors as follows:
Look at the request header information:
You can see that the request interceptor we developed has taken effect.
Four, use in Taro
Since Taro has provided Taro.request method as the request method, we do not need to use axios to send the request.
The transformation based on the above code is also very simple, only two places need to be changed:
1. Modify the method of packaging request
The main thing is to replace axios with the Taro.request method, and use 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; // 看需求,是否需要
2. Modify the interceptor scheduler
Since axios and Taro.request
different methods of adding interceptors, they also need to be replaced:
import request from './interceptors/request';
import response from './interceptors/response';
export const interceptor = {
request,
response
};
export const getInterceptor = (chain = {}) => {
// 设置请求拦截器
let requestParams = chain.requestParams;
for (const key in request) {
requestParams = request[key](requestParams);
}
// 设置响应拦截器
let responseObject = chain.proceed(requestParams);
for (const key in response) {
responseObject = responseObject.then(res => response[key](res));
}
return responseObject;
};
For the specific API, see Taro.request document, but I don’t need to introduce it here.
5. Project summary and thinking
This refactoring is mainly based on the existing business, so even after the refactoring of the request layer, there are still many points that can be optimized. At present, I think that there are these, which can be regarded as one of my TODO LISTs:
1. Separate the request layer into a library
Since the company now has many projects on independent sites, considering the unified development specifications of the projects, the request layer can be considered as a private library for maintenance.
Current thinking:
- Refer to the plug-in architecture design, and manage all interceptors lerna
- Upgrade TypeScript to facilitate management and development;
- Carry out engineering transformation, adding build tools, unit tests, UMD, etc.;
- Use documentation and development documentation are perfect.
2. Support replaceable request library
In terms of extracting this separately, it is because our front-end team currently uses more request libraries and is relatively scattered, so considering the versatility, it is necessary to add support for replaceable request library methods.
Current thinking:
- request library adaptation layer in the existing request layer to define a unified interface;
- Built-in adaptation of several common request libraries.
3. Develop interceptor scaffolding
The purpose of this is actually very simple. Let other people in the team directly use the scaffolding tool to quickly create an interceptor according to the built-in scaffolding template, carry out subsequent development, and unify the development specifications of the interceptor to a large extent.
Current thinking:
- Built-in two sets of interceptor templates: request interceptor and response interceptor;
- Scaffolding development is relatively simple, and the parameters (such as language) are determined according to business needs.
4. Enhanced interceptor scheduling
The current implementation of this function is still relatively simple, we still have to consider enhancing interceptor scheduling.
Current thinking:
- Handling the failure of the interceptor;
- Deal with the issue of interceptor scheduling sequence;
- Interceptor synchronous execution, asynchronous execution, concurrent execution, loop execution, etc.;
- Pluggable interceptor scheduling;
- Consider referring to the Tapable plug-in mechanism;
6. Summary of this article
This article summarizes a request-level interceptor scheduling scheme through a simple project reconstruction. The purpose is to achieve a single interceptors, facilitate maintenance, and to maintain and automatically, which greatly reduces the interception of the actual business of The difficulty of getting started with the development of the device.
I still have a lot to optimize in the future. As a TODO LIST of my own, if it is made completely universal, the positioning may be more inclined to the interceptor scheduling container. Only some universal interceptors are provided, and the rest is still defined by the developer, library Responsible for scheduling, but the commonly used request libraries are generally ready, so the value of doing so needs to be weighed.
Of course, it is still prioritized to develop and use as a team's internal private library, because basically the business used within the team is similar, but the project is different.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。