Axios TS version package

Vue3.0 + TS has been used in recent projects, so some of the tools used globally have been packaged. Axios is the first tool for HTTP requests.

Encapsulation thinking

  1. The basic configuration of Axios
  2. Request interception encapsulation
  3. Encapsulation of response interception
  4. Cancel duplicate requests
  5. Genericization of request parameters and return parameters – this is done to maximize the benefits of type detection and type inference provided by TS

Learn about the Axios declaration file

The quickest way to understand a plugin is to know its declaration file. It can tell you what methods it provides, what parameters it provides and what types of parameters it provides.

  1. First, you can see how axios supports requestsMethod
  2. The type supported by the response dataResponseType
  3. Request configurationAxiosRequestConfig, which includes the request mode, request type, parameters, and some request header configuration
  4. Response data typeAxiosResponse, you can see the data structure of the AXIos response data
  5. The data type of the request exceptionAxiosError
  6. A method to cancel a request
  7. AxiosSupported by the API
export type Method =
  | 'get' | 'GET'
  | 'delete' | 'DELETE'
  | 'head' | 'HEAD'
  | 'options' | 'OPTIONS'
  | 'post' | 'POST'
  | 'put' | 'PUT'
  | 'patch' | 'PATCH'
  | 'purge' | 'PURGE'
  | 'link' | 'LINK'
  | 'unlink' | 'UNLINK'

export type ResponseType =
  | 'arraybuffer'
  | 'blob'
  | 'document'
  | 'json'
  | 'text'
  | 'stream'


exportinterface AxiosRequestConfig<T = any> { url? : string; method? : Method; baseURL? : string; transformRequest? : AxiosTransformer | AxiosTransformer[]; transformResponse? : AxiosTransformer | AxiosTransformer[]; headers? : Record<string, string>; params? : any; paramsSerializer? :(params: any) = >string; data? : T; timeout? : number; timeoutErrorMessage? : string; withCredentials? : boolean; adapter? : AxiosAdapter; auth? : AxiosBasicCredentials; responseType? : ResponseType; xsrfCookieName? : string; xsrfHeaderName? : string; onUploadProgress? :(progressEvent: any) = > void; onDownloadProgress? :(progressEvent: any) = > void; maxContentLength? : number; validateStatus? : ((status: number) = > boolean) | null; maxBodyLength? : number; maxRedirects? : number; socketPath? : string |null; httpAgent? : any; httpsAgent? : any; proxy? : AxiosProxyConfig |false; cancelToken? : CancelToken; decompress? : boolean; transitional? : TransitionalOptions signal? : AbortSignal; }export interface AxiosResponse<T = never>  {
  data: T; status: number; statusText: string; headers: Record<string, string>; config: AxiosRequestConfig<T>; request? : any; }export interface AxiosError<T = never> extends Error {
  config: AxiosRequestConfig; code? : string; request? : any; response? : AxiosResponse<T>; isAxiosError: boolean; toJSON:() = > object;
}

export interface Cancel {
  message: string;
}

exportinterface Canceler { (message? : string):void;
}

export interface CancelTokenStatic {
  new (executor: (cancel: Canceler) = > void): CancelToken;
  source(): CancelTokenSource;
}

export interface CancelToken {
  promise: Promise<Cancel>; reason? : Cancel; throwIfRequested():void;
}

export interface CancelTokenSource {
  token: CancelToken;
  cancel: Canceler;
}


export class Axios {
  constructor(config? : AxiosRequestConfig);
  defaults: AxiosRequestConfig;
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>; response: AxiosInterceptorManager<AxiosResponse>; }; getUri(config? : AxiosRequestConfig): string; request<T = never, R = AxiosResponse<T>> (config: AxiosRequestConfig<T>):Promise<R>; get<T = never, R = AxiosResponse<T>>(url: string, config? : AxiosRequestConfig<T>):Promise<R>;
  delete<T = never, R = AxiosResponse<T>>(url: string, config? : AxiosRequestConfig<T>):Promise<R>; head<T = never, R = AxiosResponse<T>>(url: string, config? : AxiosRequestConfig<T>):Promise<R>; options<T = never, R = AxiosResponse<T>>(url: string, config? : AxiosRequestConfig<T>):Promise<R>; post<T = never, R = AxiosResponse<T>>(url: string, data? : T, config? : AxiosRequestConfig<T>):Promise<R>; put<T = never, R = AxiosResponse<T>>(url: string, data? : T, config? : AxiosRequestConfig<T>):Promise<R>; patch<T = never, R = AxiosResponse<T>>(url: string, data? : T, config? : AxiosRequestConfig<T>):Promise<R>;
}

export interface AxiosInstance extendsAxios { (config: AxiosRequestConfig): AxiosPromise; (url: string, config? : AxiosRequestConfig): AxiosPromise; }export interface AxiosStatic extendsAxiosInstance { create(config? : AxiosRequestConfig): AxiosInstance; Cancel: CancelStatic; CancelToken: CancelTokenStatic; Axios:typeof Axios;
  readonly VERSION: string;
  isCancel(value: any): boolean;
  all<T>(values: (T | Promise<T>)[]): Promise<T[]>;
  spread<T, R>(callback: (. args: T[]) = > R): (array: T[]) = > R;
  isAxiosError(payload: any): payload is AxiosError;
}

declare const axios: AxiosStatic;

export default axios;

Copy the code

Implement a Request class

  1. First, declare the key fields
import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosError,
  AxiosResponse,
  CancelTokenStatic
} from 'axios'

class Request {
	instance: AxiosInstance / / axios instance
	pending:ArrayThe < {url: string
		cancel: Function} > = []// The request URL collection looks for the presence of the current interface request before each request, which is cancelled and a new request is created

	CancelToken: CancelTokenStatic = axios.CancelToken

	axiosRequestConfig: AxiosRequestConfig = {} // Request configuration
	successCode: Array<number> = [200.201.204] // The corresponding status code of success
	baseURL:string='/api'

	constructor() {
		// This initializes the axiOS configuration and binds the request and the corresponding interception configuration
		this.requestConfig()
	    this.instance = axios.create(this.axiosRequestConfig)
	    this.interceptorsRequest()
	    this.interceptorsResponse()
	}
	asyncget<T = any>(url: string, config? : AxiosRequestConfig):Promise<MyResponseType<T>> {}
	asyncpost<T = any>(url: string,data? : T,config? : AxiosRequestConfig):Promise<MyResponseType<T>> {}
	requestConfig():void{}
	interceptorsRequest():void{}
	interceptorsResponse():void{}
	requestConfig():void{}
	// Cancel duplicate requests
    removePending(config: AxiosRequestConfig): void{}
    // Request log
    requestLog(request: AxiosRequestConfig): void {}
    // Response log
    responseLog(response: AxiosResponse): void{}}export default new Request()
Copy the code

In fact, once the above content is completed, the general framework is complete. The next step is to add logic for each stage.

  1. The basic configuration of Axios
  2. The request interceptor needs to handle parameter types and custom request header Settings, remove previously repeated requests and add the current request interface to peding, and print the request log
  3. The response interceptor processes all status codes other than successCode, removes the current interface request from Peding, and prints a response log
  4. The implementation of the request method is to call the corresponding request from AXIos in the corresponding method and then process the response data and return it

Here is the complete code:

// import http from 'http'
// import https from 'https'
import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosError,
  AxiosResponse,
  CancelTokenStatic
} from 'axios'
import qs from 'qs'
import { store } from '.. /store'
import { MyResponseType } from './types'

export class Request {
  protected instance: AxiosInstance

  protected pending: ArrayThe < {url: string
    cancel: Function
  }> = []

  protected CancelToken: CancelTokenStatic = axios.CancelToken

  protected axiosRequestConfig: AxiosRequestConfig = {}

  protected successCode: Array<Number> = [200.201.204]

  protected baseURL: string = '/api'

  constructor() {
    this.requestConfig()
    this.instance = axios.create(this.axiosRequestConfig)
    this.interceptorsRequest()
    this.interceptorsResponse()
  }

  asyncget<T = any>(url: string, config? : AxiosRequestConfig):Promise<MyResponseType<T>> {
    try {
      const data: MyResponseType<T> = await this.instance.get(url, config)
      return data
    } catch (err: any) {
      const message = err.message || 'Request failed'
      return {
        code: -1,
        message,
        data: null as any
      }
    }
  }

  asyncpost<T = any>( url: string, data? : T, config? : AxiosRequestConfig ):Promise<MyResponseType<T>> {
    try {
      const res: MyResponseType<T> = await this.instance.post(url, data, config)
      return res
    } catch (err: any) {
      const message = err.message || 'Request failed'
      return {
        code: -1,
        message,
        data: null as any
      }
    }
  }

  // axios requests configuration
  protected requestConfig(): void {
    this.axiosRequestConfig = {
      baseURL: this.baseURL,
      headers: {
        timestamp: String(new Date().getTime()),
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
      },
      transformRequest: [(obj) = > qs.stringify(obj)],
      transformResponse: [
        (data: AxiosResponse) = > {
          return data
        }
      ],
      paramsSerializer(params: any) {
        return qs.stringify(params, { arrayFormat: 'brackets'})},timeout: 30000.withCredentials: false.responseType: 'json'.xsrfCookieName: 'XSRF-TOKEN'.xsrfHeaderName: 'X-XSRF-TOKEN'.maxRedirects: 5.maxContentLength: 2000.validateStatus(status: number) {
        return status >= 200 && status < 500
      }
      // httpAgent: new http.Agent({ keepAlive: true }),
      // httpsAgent: new https.Agent({ keepAlive: true })}}// Request interception
  protected interceptorsRequest() {
    this.instance.interceptors.request.use(
      (config: AxiosRequestConfig) = > {
        this.removePending(config)
        config.cancelToken = new this.CancelToken((c: any) = > {
          this.pending.push({
            url: `${config.url}/The ${JSON.stringify(config.data)}&request_type=${config.method}`.cancel: c
          })
        })
        const token = store.getters['userModule/getToken']
        if (token) {
          Object.assign(config.headers, { 'x-token': token || ' '})}this.requestLog(config)
        return config
      },
      (error: AxiosError) = > {
        return Promise.reject(error)
      }
    )
  }

  // Response interception
  protected interceptorsResponse(): void {
    this.instance.interceptors.response.use(
      (response: AxiosResponse) = > {
        this.responseLog(response)
        this.removePending(response.config)
        if (this.successCode.indexOf(response.status) === -1) {
          // Message({
          // message: response.data.message || 'Error',
          // type: 'error',
          // duration: 5 * 1000
          // })
          // if (response.data.code === 401) {
          // messagebox.confirm (' you have been logged out, you can cancel to remain on this page, or log in again ', 'sure to log out ', {
          //     confirmButtonText: '重新登录',
          // cancelButtonText: 'cancel ',
          // type: 'warning'
          // }).then(() => {
          // UserModule.ResetToken()
          // location.reload()
          / /})
          // }
          return Promise.reject(new Error(response.data || 'Error'))}return response.data
      },
      (error: AxiosError) = > {
        return Promise.reject(error)
      }
    )
  }

  // Cancel duplicate requests
  protected removePending(config: AxiosRequestConfig): void {
    this.pending.map((v, index) = > {
      if (v.url === `${config.url}/The ${JSON.stringify(config.data)}&request_type=${config.method}`) {
        v.cancel()
        console.log('= = = = ='.this.pending)
        this.pending.splice(index, 1)
        console.log('+ + + + +'.this.pending)
      }
      return v
    })
  }

  // Request log
  protected requestLog(request: AxiosRequestConfig): void {
    if (process.env.NODE_ENV === 'development') {
      const randomColor = `rgba(The ${Math.round(Math.random() * 255)}.The ${Math.round(
        Math.random() * 255
      )}.The ${Math.round(Math.random() * 255)}) `
      console.log(
        '% c ┍ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ┑'.`color:${randomColor}; `
      )
      console.log('| request address:, request.url)
      console.log(
        '| request parameters:,
        qs.parse(
          ((request.method || 'get').toLowerCase() === 'get' ? request.params : request.data) as any
        )
      )
      console.log(
        '% c ┕ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ┙'.`color:${randomColor}; `)}}// Response log
  protected responseLog(response: AxiosResponse): void {
    if (process.env.NODE_ENV === 'development') {
      const randomColor = `rgba(The ${Math.round(Math.random() * 255)}.The ${Math.round(
        Math.random() * 255
      )}.The ${Math.round(Math.random() * 255)}) `
      console.log(
        '% c ┍ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ┑'.`color:${randomColor}; `
      )
      console.log('| request address:, response.config.url)
      console.log('| request parameters:, qs.parse(response.config.data as any))
      console.log('| return data:, response.data)
      console.log(
        '% c ┕ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ┙'.`color:${randomColor}; `)}}}export default new Request()

Copy the code