For front-end projects, we typically use the AXIos library, a Promise-based HTTP library that runs in both the browser and Node.js, for data interaction with the back end. Because there are many excellent features, such as request and response interception, request cancellation, JSON conversion, client defense CSRF, etc., it is necessary to encapsulate an AXIOS class that fits your own business scenario to provide reuse and unified interface request normalization for future project development.

Let’s begin our axios encapsulation tour.

1. Directory structure

│ ├─ ├─ config.js │ ├─ config.js │ ├─ config.js │ ├─ config.js │ ├─ Config.js │ ├─ Config.js │ ├─ ├─ errorHandle.js // ├─ urls // / ├─ ├─ urls // // // // // // ├─ ├─ log.js │ ├─ ├─ ├ _ ├ _ ├ _ ├ _ ├ _ ├ _ ├ _ ├ _ ├ ─ index.js // class imp //Copy the code

2. Introduce the required modules

In the httprequest.js file, we’ll first introduce the modules we need as follows:

import axios from 'axios' // Introduce the AXIos library
import qs from 'qs' // The qs module is used to serialize post data
import store from '@/store' // State management, used to set tokens or call custom interface methods
import errorHandle from './errorHandle' // Unified error response handling
import { getToken } from '@/utils/auth' // Obtain the token from localStorage
Copy the code

3, HttpRequest class encapsulation

1) Define the default configuration

In the default configuration, you can define the corresponding baseURL according to the development environment or production environment. The default request mode is GET, and the data type accepted by header header and the data format sent by request is JSON. The timeout period is set to 10s.

class HttpRequest {
  // Set the default value to null to facilitate the use of the devServer agent
  constructor (baseURL = ' ') {
    this.defaultConfig = { // Default configuration
      baseURL,
      method: 'get'.headers: {
        Accept: 'application/json'.'Content-Type': 'application/json; charset=UTF-8'
      },
      timeout: 1000 * 10.// Request timeout
      isErrorHandle: false // Whether to enable the global error response prompt}}}Copy the code

2) Create an axios instance

When an AXIOS instance is created, the options configuration passed in by the user is merged with the default configuration, the defined Interceptors interceptor is called (described below), and the Axios instance is returned.

  /** * Create an axios instance **@param {Object} Options User-defined configuration *@return {Axios} Return axios instance *@memberof HttpRequest* /
  createAxiosInstance (options) {
    const axiosInstance = axios.create()
    // The default and user-defined configurations are merged
    const newOptions = this.mergeOptions(this.defaultConfig, options)
    // Call the interceptor
    this.interceptors(axiosInstance)
    // Return the instance
    return axiosInstance(newOptions)
  }

  /** * Merge configuration **@param {Object} Source Original configuration item *@param {Object} Target Indicates the target configuration item@return {Object} Return new configuration *@memberof HttpRequest* /
  mergeOptions (source, target) {
    if (typeoftarget ! = ='object' || target == null) {
      return source
    }
    return Object.assign(source, target)
  }
Copy the code

3) Interceptor setup

In the interceptor, we introduce an AXIos instance as a parameter, and then define a request interceptor and a response interceptor.

  • Request interceptor:

    • It’s a regular operation. It’s on every requesttokenAs a verification basis for interaction with back-end data;
    • We’re only dealing with the usual onesget,postRequest, as passed in from the configurationmethodProperty to perform the request dynamically (note that there is no separate encapsulation hereget,postRequest method);
    • There’s a little bit of a difference,getRequest mode Is used to transfer parametersparams: {id: xx}Form,postWay it isdata: {id: xx} ;
    • Associated with the request headerContent-TypeProperty to serialize the passed parameter object, so that the back end can normally receive the passed parameter, which needs to be set in the request header if it is an uploaded file type'Content-Type': 'multipart/form-data; '.
  • Response interceptor:

    • Interceptor processing is relatively simple, based on the server responsestatusStatus code, normal response, will respond to the datadataReturn out, exception response, will be the wholeresponseGo back out in defeat.
/** * interceptor **@param {Axios} instance
 * @memberof HttpRequest* /
interceptors (instance) {
  // Request interceptor
  instance.interceptors.request.use((config) = > {
    const { headers, method, params, data } = config
    // Each request carries a token
    const token = getToken() || ' '
    token && (headers.Authorization = token)

    // If content-type is not 'multipart/form-data; '(File upload type)
    if(! headers['Content-Type'].includes('multipart')) {
      // If request is post, set content-type to 'Application /x-www-form-urlencoded; // Set content-type to' Application /x-www-form-urlencoded; charset=UTF-8'
      (method === 'post') && (headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8')
      // Convert data to contentType
      const contentType = headers['Content-Type']
      // content-type 'application/json; ', the server received the raw body(raw data) "{name:"nowThen",age:"18"}
      // Content-type 'application/x-www-form-urlencoded; ', the server received the raw body name=nowThen&age=18
      const paramData = (method === 'get')? params : data contentType && (config.data = contentType.includes('json')?JSON.stringify(paramData) : qs.stringify(paramData))
    }
    return config
  }, (error) = > {
    // Processing response error
    this.defaultConfig.isErrorHandle && errorHandle(error)
    return Promise.reject(error)
  })

  // Response interceptor
  instance.interceptors.response.use((response) = > {
    const { status, data } = response

    // Normal response
    if (status === 200 || (status < 300 || status === 304)) {
      if (data.code === 401) {
        // The token is incorrect or expired. You need to log in again and clear the token in Store and LocalStorge
        store.dispatch('user/toLogin') // The login page is displayed
      }
      // Return data
      return Promise.resolve(data)
    }
    return Promise.reject(response)
  }, (error) = > {
    // Processing response error
    this.defaultConfig.isErrorHandle && errorHandle(error)
    return Promise.reject(error)
  })
}
Copy the code
  • Interceptor exception handling
    • In case of request timeout, network exception/disconnection, unauthorized/access denial, etc., the encapsulated global unified error response handler is invokederrorHandle;
    • The unified global error response message is disabled by defaultisErrorHandletrueProperty to enable.

ErrorHandle is defined in the errorhandle. js file as follows:

import { message } from 'ant-design-vue'

/** * AXIos unified error handling is for HTTP status code errors *@param {Object} err* /
function errorHandle (err) {
 // Determine the server response
 if (err.response) {
   switch (err.response.status) {
     // The user has no permission to access the interface
     case 401:
       message.error('Unauthorized, please log in ~')
       break
     case 403:
       message.error('Server denied access ~')
       break
     case 404:
       message.error('Requested resource does not exist ~')
       break
     case 500:
       message.error('Server exception, please try again later ~')
       break}}else if (err.message.includes('timeout')) {
   message.error('Connection timeout ~')}else if (
   err.code === 'ECONNABORTED' ||
   err.message === 'Network Error' ||
   !window.navigator.onLine
 ) {
   message.error('Network disconnected, please check connection ~')}else {
   // Perform other processing
   console.log(err.stack)
 }
}

export default errorHandle

Copy the code

4) Export the HttpRequest class

Export the HttpRequest class at the end of httprequest.js:

export default HttpRequest
Copy the code

4, HttpRequest class use

1) API reference entry file

In the index.js entry file, we first import the wrapped instance, and then use webPack’s require.context API to automatically import the defined interface url, so we don’t have to manually import one by one. Use the require.context API for reference in my other article: automatic registration of Vue components [require.context]

import api from './request/axiosApi' // Introduce the AXIos wrapper instance

// https://webpack.js.org/guides/dependency-management/#requirecontext
const apiFiles = require.context('./urls'.true./\.js$/)

// Automatically loads all configured interfaces in the urls directory
const apiRequest = apiFiles.keys().reduce((apis, apiPath) = > {
  const name = apiPath.replace(/^\.\/(.*)\.\w+$/.'$1')
  const value = apiFiles(apiPath)

  apis[name] = Object.keys(value.default).reduce((prev, cur) = > {
    prev[cur] = (options = {}) = >api.createAxiosInstance({ ... value.default[cur], ... options })return prev
  }, {})

  return apis
}, {})

// console.log(apiRequest)

export default apiRequest
Copy the code

Of course, the above is automatic import, but it introduces the entire interface object, and sometimes we need to import on demand, we can do this:

import api from './request/axiosApi' // Introduce the AXIos wrapper instance

import users from './urls/users' // Import the Users API configuration

const createApi = (apiUrls) = > {
  return Object.keys(apiUrls).reduce((prev, cur) = > {
    prev[cur] = (options = {}) = >axios.createAxiosInstance({ ... apiUrls[cur], ... options })return prev
  }, {})
}

export const user = createApi(users)
// Other interfaces...
Copy the code

2) Interface URL file

Take users.js as an example:

export default {
  login: {
    method: 'post'.url: 'user/login'
  },
  logout: {
    method: 'post'.url: 'user/logout'
  },
  getInfo: {
    method: 'get'.url: 'user/getInfo'},... }Copy the code

3) Axios instance export file

AxiosApi. Js:

import config from './config'
import HttpRequest from './HttpRequest'

// Get the API URL root based on the current environment
const baseURL = process.env.NODE_ENV === 'production' ? config.baseURL.prod : config.baseURL.dev
// Create an HtpRequest object instance
const axios = new HttpRequest(baseURL)

export default axios
Copy the code

4) API use

  • Introduced the global

In the project main.js entry file:

import apiRequest from './api' // Introduce encapsulated interface objects
Vue.prototype.$api = apiRequest
Copy the code

Examples of interface requests used in components:

this.$api.user.getInfo({
	params: {id: xx}
  })
  .then((res) = > {
  	// Data processing...
  })
  .catch((error) = > {
  	// Exception handling...
  })
Copy the code
  • According to the need to introduce

Where in use, an example interface request:

import { user } from './api' // Import encapsulated interface objects as needed
user.getInfo({
	params: {id: xx}
  })
  .then((res) = > {
  	// Data processing...
  })
  .catch((error) = > {
  	// Exception handling...
  })
Copy the code

5, summary

Ok, take out the AXIos package at the bottom of the box and sort it out a little, so this article comes into being. For different project business needs, there may be some areas that need to be improved. If you have better package and idea, welcome to put forward optimization suggestions in the introduction below.