Step-by-step explanation

The installationaxios,qs

yarn add axios
yarn add qs
Copy the code

axiosconfiguration

Encapsulate AXIos. Since AXIos is a plug-in, create a new plugins folder to place:

// ./src/plugins/axios/axiosConfigs.ts
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import store from '@/store';
import { ElMessage } from "element-plus";

// Create an instance of Axios
const service = axios.create({
    // baseURL will automatically precede url unless the url is an absolute URL
    baseURL: process.env.NODE_ENV === 'production' ? ` / ` : '/api'.headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
    },
    withCredentials: false.// Whether credentials are required for cross-domain requests
    timeout: 30000.// 'validateStatus' defines a resolve or reject PROMISE for a given HTTP response status code.
    validateStatus() {
        // 'validateStatus' returns' true' (or set to 'null' or 'undefined'), and promise will be resolved; Otherwise, the promise will be rejecte
        // Use async-await to handle reject, so return resolve to handle exception in business code
        return true;
    },

    // 'transformResponse' allows the response data to be modified before being passed to then/catch
    transformResponse: [(data) = > {
        if (typeof data === 'string' && data.startsWith('{')) {
            data = JSON.parse(data);
        }
        returndata; }}]);// Add request interceptor
service.interceptors.request.use((config: AxiosRequestConfig) = > {
    // console.log(' before sending a request ', config.url);

    // Get the token and add it to the request header
    let token = store.state.user.token;
    if(token){
        config.headers.Authorization = token;
        // config.headers.Authorization = 'Bearer ' + token;
    }

    return config;
}, (error: any) = > {
    // console.log(' send request error ', error.response, error.data);

    // Error thrown to business code
    error.data = {
        message: 'Server exception, please contact administrator! '
    };

    return Promise.reject(error);
});

// Add a response interceptor
service.interceptors.response.use((response: AxiosResponse) = > {
    // console.log(' response interception ', response.status, response);

    /* Handle HTTP errors, throw business code */
    const status = response.status;
    const decide = status < 200 || status >= 300;
    if (decide) {
        const message = showStatus(status);
        // console.log(" handle HTTP errors ", message);
        if (typeof response.data === 'string') {
            response.data = { message };
        } else {
            response.data.message = message;
        }
        ElMessage({
            message,
            type: 'error'.showClose: true
        })
        return Promise.reject(response.data);
    }

    return response;
}, (error: any) = > {
    // console.log(' request error ', error, axios.iscancel (error), error.message);

    if (axios.isCancel(error)) {
        // console.log(' repeated request: '+ error.message);
        ElMessage({
            message: 'Do not repeat requests'.type: 'warning'.showClose: true
        });
    } else {
        const message = 'Request timed out or server exception, please check network or contact administrator! ';
        ElMessage({
            message,
            type: 'error'.showClose: true
        });
    }

    return Promise.reject(error);
});

const showStatus = (status: number) = > {
    let message = ' ';
    switch (status) {
        case 400:
            message = 'Request error (400)';
            break;
        case 401:
            message = 'Not authorized, please log back in (401)';
            break;
        case 403:
            message = 'Access denied (403)';
            break;
        case 404:
            message = 'Request error (404)';
            break;
        case 408:
            message = 'Request timed out (408)';
            break;
        case 500:
            message = 'Server error (500)';
            break;
        case 501:
            message = 'Service Not realized (501)';
            break;
        case 502:
            message = 'Network Error (502)';
            break;
        case 503:
            message = 'Service unavailable (503)';
            break;
        case 504:
            message = 'Network Timeout (504)';
            break;
        case 505:
            message = 'HTTP version not supported (505)';
            break;
        default:
            message = 'Connection error (${status})! `;
    }
    return message;
    // return '${message}, please check the network or contact administrator! `
};

export default service;
Copy the code

encapsulationaxiosrequest

Format request and response data

Set the format of the response data, set the format of the get, POST and other methods:

// ./src/plugins/axios/types.ts
import { AxiosResponse, AxiosRequestConfig } from 'axios';

// Network request response format, T is the specific interface return type data
interface CustomSuccessData<T> {
    code: number; msg? :string; message? :string; data? : T; [keys:string] :any;
}

interface Get {
    <T>(url: string, config? : AxiosRequestConfig):Promise<CustomSuccessData<T>>;
}

interface Post {
    <T>(url: string, params? :string | object, config? : AxiosRequestConfig):Promise<CustomSuccessData<T>>;
}

/ /... Delete, etc.

export {
    CustomSuccessData,
    Get,
    Post
}
Copy the code

encapsulationaxiosRequest method

Encapsulate get, POST and other methods and call them using request:

// ./src/plugins/axios/request.ts
import service from '@/plugins/axios/axiosConfigs'
import { Get, Post } from './types'; // Interface generics

// Encapsulate the get method of type GET
const get: Get = async (url, config) => {
    const response = awaitservice.get(url, { ... config});return response.data;
};

const post: Post = async (url, params, config) => {
    const response = awaitservice.post(url, params, {... config});return response.data;
};

/ /... Delete, etc.

// Use request for unified invocation
const request = {
    get,
    post
};

export default request;
Copy the code

Interface configuration

Configure API interface, add API folder:

// ./src/api/httpUrl.ts
const config: Api = {
    rootUrl: "http://localhost:8080/"};const httpApi: Api =  {
    // Test the interface
    banner: config.rootUrl + 'home/banner'.// banner
    login: config.rootUrl + 'user/login'.// User login
}

export default httpApi;
Copy the code

Request interface configuration:

// ./src/api/requestApi.ts
import request from '@/plugins/axios/request'; / / axios encapsulation
import '@/utils/interfaces/AjaxResponse'; // Back-end response data interface
import '@/utils/interfaces/AjaxRequest'; // The front-end request data interface
import httpUrl from "./httpUrl"; Url / / interface
import qs from "qs";

/ / get the banner
const getBanner = async() = > {return await request.get<Array<AjaxResponse.Banner>>(httpUrl.banner);
}

// User login
const login = async (params: AjaxRequest.login) => {
    return await request.post<string>(httpUrl.login, qs.stringify(params));
}

const handleError = (err: any) = > {
    // console.log(" error request ", err);
    throw err;
}

export {
    getBanner,
    login,
    handleError
}
Copy the code

use

<template>
    <div class="home">
        {{num}}
        <el-button @click="apiTest">Test button</el-button>
    </div>
</template>

<script lang="ts" setup>
import { getBanner, login, handleError } from '@/api/requestApi'; // Request the interface

const num = ref(100);

const apiTest = async() = > {console.log("apiTest");

    / / get request
    const banner = await getBanner().catch(handleError);
    console.log('banner', banner);

    / / post request
    // It will then move to vuex actions
    let logintest = await login({
        tel: '13430046832'.// Type it randomly
        password: '123456'
    }).catch(handleError);
    console.log("logintest", logintest);

}
</script>
Copy the code

Error test

Set the request header to ‘content-type ‘: ‘application/json; Charset =utf-8’;

Cancel duplicate requests

Set the method for adding/removing/emptying requests:

// ...
import qs from 'qs';

const service = axios.create({
    // ...
});

// Declare a Map to store the identity and cancel functions for each request
const pending = new Map(a);/ * * *@description: Add request *@param {AxiosRequestConfig} config* /
const addPending = (config: AxiosRequestConfig) = > {
    const url = [
        config.method,
        config.url,
        qs.stringify(config.params),
        qs.stringify(config.data)
    ].join('&');
    
    config.cancelToken = config.cancelToken || new axios.CancelToken(cancel= > {
        if(! pending.has(url)) {// If there is no current request in Pending, add it
            // console.log(" No current request in request queue, add request ")pending.set(url, cancel); }})}/ * * *@description: Remove request *@param {AxiosRequestConfig} config
 * @return {*}* /
let isRemove = false
const removePending = (config: AxiosRequestConfig) = > {
    const url = [
        config.method,
        config.url,
        qs.stringify(config.params),
        qs.stringify(config.data)
    ].join('&');

    if (pending.has(url)) { // If the current request identifier exists in pending, cancel the current request and remove it
        // console.log(" The current request already exists in the request queue, will not be repeated ");
        const cancel = pending.get(url);
        isRemove = true
        cancel(url); // Cancel the requestpending.delete(url); }}/ * * *@description: Clears pending requests (called when a route jumps) */
export const clearPending = () = > {
    // console.log(" Empty request queue ");
    for (const [url, cancel] of pending) {
        cancel(url); // Cancel the request
    }
    pending.clear(); // Empty the request
}
Copy the code

Added to interceptor:

// Add request interceptor
service.interceptors.request.use((config: AxiosRequestConfig) = > {
    // console.log(' before sending a request ', config.url);

    removePending(config) // Check and cancel the previous request before the request starts
    addPending(config) // Add the current request to Pending

    // ...

    return config;
}, (error: any) = > {
    // ...
});

// Add a response interceptor
service.interceptors.response.use((response: AxiosResponse) = > {
    // console.log(' response interception ', response.status, response);

    removePending(response) // Remove the request after the request is complete

    // ...

}, (error: any) = > {
    if (axios.isCancel(error)) { // Cancel the request
        if(isRemove){ // No prompt is displayed for cancellation requests caused by route switching
            ElMessage({
                message: 'Do not repeat requests'.type: 'warning'.showClose: true
            });
            isRemove = false}}else {
        // ...
    }
    
    // ...
});
Copy the code

Undo all requests on a route jump

Undo all requests on a route jump. / SRC /router/index.ts add:

// ./src/router/index.ts
import { clearPending } from "@/plugins/axios/axiosConfigs";
import store from '@/store'

// ...

// Route guard
router.beforeEach((to, from, next) = > {

    clearPending(); // Clear all requests before jumping to the route

    if(to.meta.isAuth){
        const token = store.state.user.token;
        if(token){
            next();
        }else{
            next({
                name: 'login'}); }}else{ next(); }});// ...
Copy the code

Axios configures the complete code

// ./src/plugins/axios/axiosConfigs.ts
/* * @Author: una * @Date: 2021-06-16 17:16:12 * @LastEditors: una * @LastEditTime: 2021-07-09 14:51:28 * @Description: Encapsulation axios * /
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import store from '@/store';
import { ElMessage } from "element-plus";
import qs from 'qs';

// Create an instance of Axios
const service = axios.create({
    // baseURL will automatically precede url unless the url is an absolute URL
    baseURL: process.env.NODE_ENV === 'production' ? ` / ` : '/api'.headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
        // 'Content-Type': 'application/json; charset=utf-8'
    },
    withCredentials: false.// Whether credentials are required for cross-domain requests
    timeout: 30000.// 'validateStatus' defines a resolve or reject PROMISE for a given HTTP response status code.
    validateStatus() {
        // 'validateStatus' returns' true' (or set to 'null' or 'undefined'), and promise will be resolved; Otherwise, the promise will be rejecte
        // Use async-await to handle reject, so return resolve to handle exception in business code
        return true;
    },

    // 'transformResponse' allows the response data to be modified before being passed to then/catch
    transformResponse: [(data) = > {
        if (typeof data === 'string' && data.startsWith('{')) {
            data = JSON.parse(data);
        }
        returndata; }}]);// Declare a Map to store the identity and cancel functions for each request
const pending = new Map(a);/ * * *@description: Add request *@param {AxiosRequestConfig} config* /
const addPending = (config: AxiosRequestConfig) = > {
    const url = [
        config.method,
        config.url,
        qs.stringify(config.params),
        qs.stringify(config.data)
    ].join('&');

    config.cancelToken = config.cancelToken || new axios.CancelToken(cancel= > {
        if(! pending.has(url)) {// If there is no current request in Pending, add it
            // console.log(" No current request in request queue, add request ")pending.set(url, cancel); }}); }/ * * *@description: Remove request *@param {AxiosRequestConfig} config
 * @return {*}* /
let isRemove = false
const removePending = (config: AxiosRequestConfig) = > {
    const url = [
        config.method,
        config.url,
        qs.stringify(config.params),
        qs.stringify(config.data)
    ].join('&');

    if (pending.has(url)) { // If the current request identifier exists in pending, cancel the current request and remove it
        // console.log(" The current request already exists in the request queue, will not be repeated ");
        const cancel = pending.get(url);
        isRemove = true
        cancel(url); // Cancel the requestpending.delete(url); }}/ * * *@description: Clears pending requests (called when a route jumps) */
export const clearPending = () = > {
    // console.log(" Empty request queue ");
    for (const [url, cancel] of pending) {
        cancel(url); // Cancel the request
    }
    pending.clear(); // Empty the request
}

// Add request interceptor
service.interceptors.request.use((config: AxiosRequestConfig) = > {
    // console.log(' before sending a request ', config.url);

    removePending(config); // Check and cancel the previous request before the request starts
    addPending(config); // Add the current request to Pending

    // Get the token and add it to the request header
    const token = store.state.user.token;
    if(token){
        config.headers.Token = token;
        // config.headers.Authorization = 'Bearer ' + token;
    }

    return config;
}, (error: any) = > {
    // console.log(' send request error ', error.response, error.data);

    // Error thrown to business code
    error.data = {
        message: 'Server exception, please contact administrator! '
    };

    return Promise.reject(error);
});

// Add a response interceptor
service.interceptors.response.use((response: AxiosResponse) = > {
    // console.log(' response interception ', response.status, response);

    removePending(response); // Remove the request after the request is complete

    /* Handle HTTP errors, throw business code */
    const status = response.status;
    const decide = status < 200 || status >= 300;
    if (decide) { / / HTTP error
        const message = showStatus(status);
        // console.log(" handle HTTP errors ", message);
        if (typeof response.data === 'string') {
            response.data = { message };
        } else {
            response.data.message = message;
        }
        ElMessage({
            message,
            type: 'error'.showClose: true
        });
        return Promise.reject(response.data);
    }else { // The interface is successfully connected
            if(response.data.code == 200) {return response.data
            }else { // The interface reported an error
                if(response.config.url){
                    if(response.config.url.indexOf('login') > -1){
                        store.commit('user/SET_LOGIN_ERR_MSG', response.data.data)
                        store.commit('user/SET_TOKEN'.' ')}else{
                        ElMessage({
                            message: response.data.data || response.data.message,
                            type: 'error'.showClose: true
                        });
                    }
                    throw response;// Throw an error}}}},(error: any) = > {
    // console.log(' request error ', error, axios.iscancel (error), error.message);

    if (axios.isCancel(error)) { // Cancel the request
        if(isRemove){ // No prompt is displayed for cancellation requests caused by route switching
            // console.log(' repeated request: '+ error.message);
            ElMessage({
                message: 'Do not repeat requests'.type: 'warning'.showClose: true
            });
            isRemove = false}}else {
        const message = 'Request timed out or server exception, please check network or contact administrator! ';
        ElMessage({
            message,
            type: 'error'.showClose: true
        });
    }

    return Promise.reject(error);
});

const showStatus = (status: number) = > {
    let message = ' ';
    switch (status) {
        case 400:
            message = 'Request error (400)';
            break;
        case 401:
            message = 'Not authorized, please log back in (401)';
            break;
        case 403:
            message = 'Access denied (403)';
            break;
        case 404:
            message = 'Request error (404)';
            break;
        case 408:
            message = 'Request timed out (408)';
            break;
        case 500:
            message = 'Server error (500)';
            break;
        case 501:
            message = 'Service Not realized (501)';
            break;
        case 502:
            message = 'Network Error (502)';
            break;
        case 503:
            message = 'Service unavailable (503)';
            break;
        case 504:
            message = 'Network Timeout (504)';
            break;
        case 505:
            message = 'HTTP version not supported (505)';
            break;
        default:
            message = 'Connection error (${status})! `;
    }
    return message;
    // return '${message}, please check the network or contact administrator! `
};

export default service;
Copy the code

Extension: Set global variables

Custom properties are added to each component instance and can be accessed through this (not recommended) :

import { createApp } from 'vue'
import App from './App.vue'
import route from './route'

// Configure the request data
import { AxiosInstance } from "axios";
import Axios from "axios";

// Configure Axios globally
declare module '@vue/runtime-core' {
    interface ComponentCustomProperties {
        $axios: AxiosInstance; }}let app = createApp(App)
app.config.globalProperties.$axios = Axios;  // this.Axios
app.use(route)
app.mount('#app')
Copy the code

Use:

import httpUrl from "@/api/httpUrl"; Url / / interface

// ...

mounted(){

    const apiTest = () = > {

        // eslint-disable-next-line no-unexpected-multiline
        (this as any).$axios.get(httpUrl.banner).then((res: any) = > {
            let data = res.data.data
            console.log("banner", data)
        }).catch((error: any) = > {
            console.log('Failed to get home page banner', error.)
        })

    }

    apiTest()
}
Copy the code

This is only an extension and is not recommended.

Refer to the article