Achieve axios interceptor

Interceptor Design and Implementation

# Requirements Analysis

We hope to make interception of sending and responding to requests, that is, before sending the request and receiving a response after doing some additional logic to.

We wanted to use the interceptors are as follows:

// 添加一个请求拦截器
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); }); 

In  axios there is a target on  interceptors the object attribute another  request and  response two properties, they have a  use method, use the method supports two parameters, the first parameter Promise similar  resolve function, the second parameter Promise similar  reject function. We can  resolve function and  reject perform synchronization code or function code logic is asynchronous.

And we can add multiple interceptor, the interceptor's execution order is the way followed by the implementation of the chain. For the  request interceptor, after addition of the interceptor will first perform the process before the request; for  response interceptor, first add the interceptor will first executed after the response.

axios.interceptors.request.use(config => { config.headers.test += '1' return config }) axios.interceptors.request.use(config => { config.headers.test += '2' return config }) 

In addition, we can also support delete a blocker, as follows:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/}) axios.interceptors.request.eject(myInterceptor) 

# The overall design

Let's use a diagram to show how the interceptor workflow:

interceptor

The whole process is a chain called way, and each interceptor can support synchronous and asynchronous processing, we naturally think of ways to use Promise chain to implement the call processing.

In the process performed Promise chain, the request interceptor  resolve function processing is  config the object, and the corresponding interceptor  resolve function processing is  response the object.

In the understanding of the interceptor workflow, we must first create an interceptor management class that allows us to add and delete traverse interceptors.

# Interceptor management class implementation

According to requirements, axios has a  interceptors target property, which there  request and  response two properties, which provide external a  use way to add interceptors, we can put both attributes seen as an interceptor managed objects. use The method that supports two arguments, the first is a  resolve function, a second  reject function for the  resolve function parameters, it is intercepting the request  AxiosRequestConfig type, and the response are interceptors  AxiosResponse type; for  reject parameter type is a function of  any the type.

According to the above analysis, we first define what interceptor managed objects external interface.

# Interface definitions

types/index.ts

export interface AxiosInterceptorManager<T> { use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number eject(id: number): void } export interface ResolvedFn<T=any> { (val: T): T | Promise<T> } export interface RejectedFn { (error: any): any } 

Here we define the  AxiosInterceptorManager generic interface, as for the  resolve function parameters, and in response to the request interceptor interceptors are different.

# Code implementation

import { ResolvedFn, RejectedFn } from '../types' interface Interceptor<T> { resolved: ResolvedFn<T> rejected?: RejectedFn } export default class InterceptorManager<T> { private interceptors: Array<Interceptor<T> | null> constructor() { this.interceptors = [] } use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number { this.interceptors.push({ resolved, rejected }) return this.interceptors.length - 1 } forEach(fn: (interceptor: Interceptor<T>) => void): void { this.interceptors.forEach(interceptor => { if (interceptor !== null) { fn(interceptor) } }) } eject(id: number): void { if (this.interceptors[id]) { this.interceptors[id] = null } } } 

We define a  InterceptorManager generic class, internal maintains a private property  interceptors, it is an array to store the interceptor. The class also provides three methods of external, in which  use the interface is added to the interceptor  interceptorsand returns a  id for deletion; forEach the interface is to walk  interceptors with, it passed a support function, traversing process will call the function, and to every a  interceptor as a parameter of the function passed; eject that is, delete an interceptor, the interceptor by passing  id deleted.

# Chained calls to achieve

Promise This section requires you to grasp and understand, can go  mdn  learning.

When we manage to achieve a good interceptor class, the next step is in  Axios the definition of a  interceptors property, its type as follows:

interface Interceptors {
  request: InterceptorManager<AxiosRequestConfig> response: InterceptorManager<AxiosResponse> } export default class Axios { interceptors: Interceptors constructor() { this.interceptors = { request: new InterceptorManager<AxiosRequestConfig>(), response: new InterceptorManager<AxiosResponse>() } } } 

Interceptors Type 2 has property, a management request interceptor class instance, in response to a management class interceptor instance. We instantiate the  Axios class, in its constructor to initialize this  interceptors instance properties.

Next, we modify  request the logical method to add logic interceptor chain calls:

core/Axios.ts

interface PromiseChain {
  resolved: ResolvedFn | ((config: AxiosRequestConfig) => AxiosPromise) rejected?: RejectedFn } request(url: any, config?: any): AxiosPromise { if (typeof url === 'string') { if (!config) { config = {} } config.url = url } else { config = url } const chain: PromiseChain[] = [{ resolved: dispatchRequest, rejected: undefined }] this.interceptors.request.forEach(interceptor => { chain.unshift(interceptor) }) this.interceptors.response.forEach(interceptor => { chain.push(interceptor) }) let promise = Promise.resolve(config) while (chain.length) { const { resolved, rejected } = chain.shift()! promise = promise.then(resolved, rejected) } return promise } 

First, a configuration of  PromiseChain the type of array  chainand the  dispatchRequest functions assigned to the  resolved attribute; first traversal request interceptor then inserted into the  chain front; then traverse the response blocker is inserted into  chain the back.

Next, the definition of an already resolve  promise, this cycle  chain, each interceptor get objects to their  resolved functions and  rejected add functions to  promise.then the parameter, this is equivalent to the chain through invocation Promise, to achieve a layer interceptors the effect of chained calls.

Note that we interceptor execution order, request interceptors, perform post-added, and then performing first added; the response blocker added to the first implementation, added after execution.

# Write demo

In  examples Creating directory  interceptor directory in the  interceptor created directory  index.html:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="utf-8"> <title>Interceptor example</title> </head> <body> <script src="/__build__/interceptor.js"></script> </body> </html> 

Then create  app.ts a file entry:

import axios from '../../src/index'

axios.interceptors.request.use(config => { config.headers.test += '1' return config }) axios.interceptors.request.use(config => { config.headers.test += '2' return config }) axios.interceptors.request.use(config => { config.headers.test += '3' return config }) axios.interceptors.response.use(res => { res.data += '1' return res }) let interceptor = axios.interceptors.response.use(res => { res.data += '2' return res }) axios.interceptors.response.use(res => { res.data += '3' return res }) axios.interceptors.response.eject(interceptor) axios({ url: '/interceptor/get', method: 'get', headers: { test: '' } }).then((res) => { console.log(res.data) }) 

The demo we added three request interceptors, added 3 interceptors response and removes a second. We run the demo accessed through a browser, we sent a request to add a  test request header, its value is  321; our response data is returned  hello, after the response processing interceptor, our final output data is  hello13.

At this point, we have to  ts-axios realize the interceptor function, it is a very useful feature, in practical work, we can use it to do some of the requirements, such as logging certification authority.

We currently by  axios sending a request, they tend to pass a bunch of configuration, but we also want  ts-axios itself will have some default configuration, we pass the user's custom configuration and the default configuration to do one merger. In fact, most of the JS libraries are similar play. The following chapter we have to implement this feature.

Guess you like

Origin www.cnblogs.com/QianDingwei/p/11403923.html