As shown in the picture, you really want to say, What F~U~C~K!!

Getting back to the point, the main purpose of axios packaging and API management is to help simplify the code and facilitate maintenance.

Encapsulation of Axios

In the VUE project, we usually use the Axios library, which is a Promise-based HTTP library that runs on the browser side and in Node.js. It has many excellent features, such as intercepting request and response, canceling request, converting JSON, client defense XSRF, and so on. Therefore, our Ue-Database also gave up the maintenance of its official library Vue-Resource and directly recommended the use of axios library. If you are not already familiar with Axios, you can refer to the Axios documentation.

The installation

npm install axios; / / install axiosCopy the code

The introduction of

I usually create a request folder in the SRC directory of my project, and then create a new http.js file and an api.js file inside. The http.js file is used to encapsulate our AXIos, and the api.js file is used to manage our interfaces.

// Import axios from http.js'axios'; // Import axios import QS from'qs'; // Introduce the QS module for serializing post type data. The toast prompt component of vant will be mentioned later. You can change it according to your UI component. import { Toast } from'vant'; 
Copy the code

Environment switch

Our project environment may include development environment, test environment, and production environment. We match our default interface URL prefix with node’s environment variable. Axios.defaults. baseURL can set the default request address for Axios.

// Environment switchif (process.env.NODE_ENV == 'development') {    
    axios.defaults.baseURL = 'https://www.baidu.com'; }else if (process.env.NODE_ENV == 'debug') {    
    axios.defaults.baseURL = 'https://www.ceshi.com';
} 
else if (process.env.NODE_ENV == 'production') {    
    axios.defaults.baseURL = 'https://www.production.com';
}Copy the code

Setting request timeout


axios.defaults.timeout = 10000;Copy the code

Setting the POST request header

Post request, we need to add a request header, so you can do a default setting here, that is, set post request header as Application/X-wwW-form-urlencoded; charset=UTF-8

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';Copy the code

  • Request to intercept

We can intercept a request before sending it. Why intercept it? What do we intercept the request for? For example, some requests can be accessed only after the user has logged in, or post requests need to serialize the data we submit. At this point, we can do an interception before the request is sent, so we can do what we want.

Request to intercept

// Import from vuex, because we need to use the state object in vuex. // Import store from vuex'@/store/index'; / / request interceptor axios. Interceptors. Request. Use (config = > {/ / whether there is in every time before sending a request to judge vuex token / / if it exists, is unified in the HTTP request header and token, // If the token exists locally, it may be expired. Therefore, the response interceptor should determine the return state const token = store.state.token; // If the token exists locally, it may be expired. token && (config.headers.Authorization = token);return config;    
    },    
    error => {        
        return Promise.error(error);    
})
Copy the code

After login, the user’s token is stored locally via localStorage or cookie. Then each time the user accesses the page (i.e. Main.js), the user will first read the token from the localStorage. If the token exists, the user has logged in. Update the token status in vuEX. Then, each time you request the interface, you will carry the token in the request header. The backend can determine whether your login has expired based on the token you carry. If you do not carry the token, you have not logged in. At this time, some friends may have questions, that is, each request carries a token, so if a page does not need the user login can access how to do? In fact, your front-end request can carry a token, but the backend can choose not to receive ah!

Interception of response

/ / response interceptor axios. Interceptors. Response. Use (response = > {/ / if the returned a status code of 200, shows that interface request is successful, can be normal to get the data / / otherwise throw an errorif (response.status === 200) {            
            return Promise.resolve(response);        
        } else {            
            returnPromise.reject(response); }}, / / the server status code is not at the beginning of 2 cases / / here you can talk with your backend developer negotiate good uniform error status code / / and then according to the returned a status code for some operation, such as a login expired, error, etc. / / here are a few common operation, Other requirements can be self-extensible error => {if(error.response.status) { switch (error.response.status) { // 401: If you are not logged in // If you are not logged in, go to the login page with the path to the current page // Return to the current page after successful login. This step needs to be performed on the login page.case 401:                    
                    router.replace({                        
                        path: '/login',                        
                        query: { 
                            redirect: router.currentRoute.fullPath 
                        }
                    });
                    break; // 403 Token expiration // Prompt users when login expires // Clear the local token and the token object in vuex // Jump to the login pagecase 403:
                     Toast({
                        message: 'Login expired, please log in again',
                        duration: 1000,
                        forbidClick: true}); / / remove the tokenlocalStorage.removeItem('token');
                    store.commit('loginSuccess', null); // Skip to the login page and upload the page to be viewed fullPath. After successful login, skip to the page to be accessedsetTimeout(() => {                        
                        router.replace({                            
                            path: '/login',                            
                            query: { 
                                redirect: router.currentRoute.fullPath 
                            }                        
                        });                    
                    }, 1000);                    
                    break; // 404 Request does not existcase 404:
                    Toast({
                        message: 'Network request does not exist',
                        duration: 1500,
                        forbidClick: true
                    });
                    break; / / other errors, directly thrown error default: Toast ({message: error. The response. The data message, duration: 1500, forbidClick:true
                    });
            }
            returnPromise.reject(error.response); }}});Copy the code

A response interceptor is the data that the server sends back to us and we can do something with it before we get it. For example, the above idea: if the status code returned by the background is 200, then the data will be returned normally. Otherwise, according to the type of the wrong status code, some errors we need are carried out. In fact, this is mainly an operation of unified processing of errors and adjusting the login page after not logging in or logging in expired.

Note that the Toast() method above is the Toast light prompt component of the Vant library that I introduced, and you use one of your prompt components according to your UI library.

Encapsulates the GET and POST methods

We commonly used Ajax request methods get, POST, put and other methods, believe that small partners are not unfamiliar. There are many similar methods for axios, if you are not clear, check the documentation. But in order to simplify our code, we still need a simple encapsulation. Here we encapsulate two main methods: GET and POST.

Get: We define a get function that takes two arguments. The first argument represents the URL we want to request, and the second argument is the request argument we want to carry. The get function returns a promise object that axios resolves when the request succeeds, and rejects the error when the request fails. Finally, the get function is thrown through export.

/** * Get method, corresponding to get request * @param {String} URL [requested URL address] * @param {Object} params [parameters carried in the request] */export function get(url, params){    
    returnnew Promise((resolve, reject) =>{ axios.get(url, { params: params }).then(res => { resolve(res.data); }).catch(err =>{ reject(err.data) }) }); }Copy the code


Post method:The principle is basically the same as get, but note that the POST method must serialize the submission from the parameter object, so here we serialize our parameters through node’s QS module. This is important because without serialization, the background will not get the data you submitted. This is the beginning of the article we
import QS from ‘qs’;The reason why. If you don’t know what serialization means, just Google it. It’s a bunch of answers.

/** * The post method corresponds to the POST request * @param {String} URL [REQUESTED URL address] * @param {Object} params [parameters carried in the request] */export function post(url, params) {
    return new Promise((resolve, reject) => {
         axios.post(url, QS.stringify(params))
        .then(res => {
            resolve(res.data);
        })
        .catch(err =>{
            reject(err.data)
        })
    });
}Copy the code

A minor detail here is that axios.get() method differs from axios.post() in how the parameters are written when the data is submitted. The difference is that the second argument to get is a {}, and the value of the params property of the object is a parameter object. The second argument to POST is a parameter object. Pay attention to the slight difference between the two!




Now that the encapsulation of AXIOS is almost complete, let’s briefly talk about unifying the API.

A neat API is like a circuit board, even if it is complex, it can clear the entire circuit. As mentioned above, we will create a new api.js file and store all of our apis in this file.

  • First, we introduce our encapsulated GET and POST methods in api.js

/** * Unified API management */ import {get, post} from'./http'Copy the code

Now, for example, we have an interface that is a POST request:

http://www.baiodu.com/api/v1/users/my_address/address_edit_beforeCopy the code

We can wrap it in api.js like this:

export const apiAddress = p => post('api/v1/users/my_address/address_edit_before', p);Copy the code

We define an apiAddress method that takes a parameter, p, which is the parameter object we carry when we request the interface. The first parameter of the post method is the address of our interface. The second parameter is the p parameter of the apiAddress, which is the parameter object carried when the interface is requested. Finally, export the apiAddress.

We can then call our API from our page like this:

import { apiAddress } from '@/request/api'; // Import our APIexport default {        
    name: 'Address'.created() { this.onLoad(); }, methods: {// Retrieve dataonLoad() {// Call the API interface and provide two arguments apiAddress({type: 0, sort: 1}). Then (res = > {/ / get the data after the success of other operations........................... })}}}Copy the code

For the rest of the API, just extend it further down in pai.js. Tips: Write a comment for each interface!!

One of the benefits of API management is that we have a unified set of apis, so if we need to change the interface later, we can find the corresponding change directly in api.js instead of having to go to every page to find our interface and then change it. The key is, in case the amount of modification is relatively large, the specification gg. In addition, if you modify the interface directly in our business code, accidentally it is easy to move our business code and cause unnecessary trouble.


Ok, finally, send the finished AXIOS encapsulation code.

/** Axios encapsulates * request interception, corresponding interception, error uniform processing */ import Axios from'axios'; import QS from'qs';
import { Toast } from 'vant';
import store from '.. /store/index'// Environment switchif (process.env.NODE_ENV == 'development') {    
    axios.defaults.baseURL = '/api';
} else if (process.env.NODE_ENV == 'debug') {    
    axios.defaults.baseURL = ' ';
} else if (process.env.NODE_ENV == 'production') {    
    axios.defaults.baseURL = 'http://api.123dailu.com/'; } axios.defaults.timeout = 10000; / / post request head axios. Defaults. Headers. Post ['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; / / request interceptor axios. Interceptors. Request. Use (config = > {/ / every time before sending a request to judge the existence of a token, if present, are unified in the HTTP request header will add token, // Even if the token exists locally, it is possible that the token is expired, so in the response interceptor to determine the return state const token = store.state.token; token && (config.headers.Authorization = token);return config;    
    },    
    error => {        
        returnPromise.error(error); }) / / response interceptor axios. Interceptors. Response. Use (response = > {if (response.status === 200) {            
            return Promise.resolve(response);        
        } else {            
            returnPromise.reject(response); }}, // The server status code is not 200 error => {if(error.response.status) { switch (error.response.status) { // 401: If you are not logged in // If you are not logged in, go to the login page with the path to the current page // Return to the current page after successful login. This step needs to be performed on the login page.case 401:                    
                    router.replace({                        
                        path: '/login',                        
                        query: { redirect: router.currentRoute.fullPath } 
                    });
                    break; // 403 Token expiration // Prompt users when login expires // Clear the local token and the token object in vuex // Jump to the login pagecase 403:                     
                    Toast({                        
                        message: 'Login expired, please log in again',                        
                        duration: 1000,                        
                        forbidClick: true}); / / remove the tokenlocalStorage.removeItem('token');                    
                    store.commit('loginSuccess', null); // Skip to the login page and upload the page to be viewed fullPath. After successful login, skip to the page to be accessedsetTimeout(() => {                        
                        router.replace({                            
                            path: '/login',                            
                            query: { 
                                redirect: router.currentRoute.fullPath 
                            }                        
                        });                    
                    }, 1000);                    
                    break; // 404 Request does not existcase 404:                    
                    Toast({                        
                        message: 'Network request does not exist',                        
                        duration: 1500,                        
                        forbidClick: true                    
                    });                    
                break; / / other errors, directly thrown error default: Toast ({message: error. The response. The data message, duration: 1500, forbidClick:true                    
                    });            
            }            
            returnPromise.reject(error.response); }}); /** * Get method, corresponding to get request * @param {String} URL [requested URL address] * @param {Object} params [parameters carried in the request] */export function get(url, params){    
    returnnew Promise((resolve, reject) =>{ axios.get(url, { params: params }) .then(res => { resolve(res.data); }) .catch(err => { reject(err.data) }) }); } /** * The post method corresponds to the POST request * @param {String} URL [REQUESTED URL address] * @param {Object} params [parameters carried in the request] */export function post(url, params) {    
    return new Promise((resolve, reject) => {         
        axios.post(url, QS.stringify(params))        
        .then(res => {            
            resolve(res.data);        
        })        
        .catch(err => {            
            reject(err.data)        
        })    
    });
}
Copy the code


If you like it, give me ❤❤ (*^▽^*)


* * * * * * * * * proves the line of * * * * * * * * * * * * * * * * * * proves the line of * * * * * * * * * * * * * * * * * * proves the line of * * * * * * * * * * * * * * * * * * a delicious line * * * * * * * * * * * * * * * * * * * * * * a delicious segmentation * * * * *

2018.8.14 update

The encapsulation of Axios varies according to the needs. Thank you very much for the pertinent suggestions in the comments, which I also thought about and improved for different needs. The main changes are as follows:

1. Optimize axiOS encapsulation to remove get and POST

2. Handle the network disconnection

3. More modular API management

4. There are multiple interface domain names

5. Mount the API to vue.prototype without importing the steps


Http.js axios encapsulation optimization, first directly paste the code:

/** * Axios encapsulates * Request interception, response interception, error unified processing */ import Axios from'axios';
import router from '.. /router';
import store from '.. /store/index';
import { Toast } from 'vant'; */ const tip = MSG => {message: MSG, duration: 1000, forbidClick:true}); } /** * Redirect to the logon page */ const toLogin = () => {router.replace({path:'/login', query: { redirect: router.currentRoute.fullPath } }); } /** ** @param {Number} status / / const errorHandle = (status, Other) => {// Check the status code. Switch (status) {// 401: indicates the login status. The login page is displayedcase 401:
            toLogin();
            break; // 403 Token expired // Clear the token and go to the login pagecase 403:
            tip('Login expired, please log in again');
            localStorage.removeItem('token');
            store.commit('loginSuccess', null);
            setTimeout(() => {
                toLogin();
            }, 1000);
            break; // 404 Request does not existcase 404:
            tip('Requested resource does not exist'); 
            break; default: console.log(other); Var instance = axios.create({timeout: 1000 * 12}); / / set the post request header instance. Defaults. Headers. Post ['Content-Type'] = 'application/x-www-form-urlencoded'; / * * * request interceptor * before every request, if there is a token is carried in the request header token * / instance. The interceptors. Request. Use (config = > {/ / login process control, Determine the login status of the user based on whether the token exists locally // But even if the token exists, it may be expired, so carry the token in each request header // The background determines the login status of the user based on the token. And gives us the corresponding status code // and then we can do some unified operations based on the status code in the response interceptor. const token = store.state.token; token && (config.headers.Authorization = token);returnconfig; }, Error = > Promise. The error (error)) / / response interceptor instance. The interceptors. Response. Use (a/res/request success = > res. Status = = = 200? Promise.resolve(res) : promise.reject (res), // request failed error => {const {response} = error;if(response) {// The request was sent, but not in the range of 2xx errorHandle(response.status, Response.data.message);return Promise.reject(response);
        } else{/ / processing / / eg: without Internet request timeout or broken network, the state of the network status update / / network state in the app. The vue controls in a tip off global network components according to hide / / the refresh to get the data about the broken network components, could explain in broken network componentsif(! window.navigator.onLine) { store.commit('changeNetwork'.false);
            } else {
                returnPromise.reject(error); }}});export default instance;Copy the code

This axios is pretty much the same as before, with a few changes:

1. Remove the encapsulation of get and POST methods, create an axios instance and export it with the export Default method, which makes it more flexible to use.

2. Removed the value of baseUrl controlled by environment variables. Considering that the interface may have several different domain names, you are prepared to control the interface domain name through the JS variable. We’ll talk about that in the API.

3. Added the processing of request timeout, that is, the disconnected state. When the network is disconnected, the display and hiding of the network disconnection prompt component can be controlled by updating the status of the network in VUEX. An outage prompt usually involves a data reload operation, which will be described in the following section.

4. Extract public functions, simplify the code, and try to ensure the principle of single responsibility.


In the API section, consider the requirements:

1. Be more modular

2. More convenient for many people to develop, effectively reduce the resolution of naming conflicts

3. Multiple interface domain names can be processed

Here we create a new API folder with an index.js and a base.js, and multiple interface JS files based on modules. Index.js is the exit of an API. Base. js manages the domain name of the interface, and other jS manages the interfaces of each module.

Let’s put the index.js code:

/** * import article from */'@/api/article'; // Interfaces for other modules... // Export the interfaceexportDefault {article, //... }Copy the code

Index.js is the exit of an API interface, so the API interface can be divided into multiple modules according to the function, which is beneficial for collaborative development. For example, one person is only responsible for the development of one module, etc., and it can also facilitate the naming of interfaces in each module.

base.js:

/** * const base = {sq:'https://xxxx111111.com/api/v1',    
    bd: 'http://xxxxx22222.com/api'
}

export default base;Copy the code

Base.js is used to manage our interface domain name, no matter how many can be defined through this interface. Even if it is modified, it is very convenient.

Finally, the description of the interface module, such as article.js above:

/** * article module interface list */ import base from'./base'; // Import interface domain name list import axios from'@/utils/http'; // Import the axios instance created in HTTP import qs from'qs'; // Import qs module const article = {// news listarticleList () {        
        return axios.get(`${base.sq}/topics`); }, // News detail, demo articleDetail (id, params) {return axios.get(`${base.sq}/topic/${id}`, { params: params }); }, // post login (params) {return axios.post(`${base.sq}/accesstoken`, qs.stringify(params)); } // Other interfaces ………… }export default article;Copy the code

1. You can use Axios more flexibly by directly introducing our encapsulated Axios instance, then defining the interface, calling the Axios instance, and returning it. For example, you can do a QS serialization of the data submitted in the POST request, etc.

2. The configuration of the request is more flexible, you can make a different configuration for a specific requirement. The Axios documentation makes it clear that the priority of the configuration is: the library defaults found in lib/defaults.js, then the instance defaults property, and then the request config parameter. The latter will take precedence over the former.

3. For restful interfaces, you can flexibly set the API address in this way.

Finally, to facilitate API calls, we need to mount it onto the vue prototype. In the main. In js:

import Vue from 'vue'
import App from './App'
import router from './router'// Import routing file import store from'./store'// Import vuex file import API from'./api'// Import API interface vue.prototype.$api= api; // Mount the API to vue's prototypeCopy the code

We can then call the interface from the page like this, eg:

methods: {    
    onLoad(id) {      
        this.$api.article.articleDetail(id, {API: 123}).then(res=> {// do something})}}.article.articleDetail(id, {API: 123}).then(res=> {// do something})}}Copy the code

Here’s a simple example of how to handle Internet disconnection:

<template>  
    <div id="app">    
        <div v-if=! "" network"Word-wrap: break-word! Important; "> <div @click="onRefresh"> </div> </div> <router-view/> </div> </template> <script> import {mapState} from'vuex';
    export default {  
        name: 'App', computed: { ... mapState(['network'])}, methods: {// Refresh the current page data by jumping to a blank page and returningonRefresh () {      
                this.$router.replace('/refresh')    
            }  
        }
    }
</script>Copy the code

This is app.vue. This is a simple demonstration of disconnection. As described in http.js, we will update the status of the network in vUE when the network is disconnected, so here we judge whether to load the disconnected component according to the status of the network. When the network is disconnected, the components on the corresponding page are loaded. When clicking “Refresh”, we reacquire the data by jumping to the Refesh page and returning immediately. So we need to create a new refresh. Vue page and return the current page in its beforeRouteEnter hook.

// refresh.vue
beforeRouteEnter (to, from, next) {
    next(vm => {            
        vm.$router.replace(from.fullPath)        
    })    
}Copy the code

This is a universal outage reminder, but it can also be customized for your project. The specific operation is a matter of opinion.

If there are more requirements, or different requirements, you can make an improvement based on your own requirements.


If it helps you, head to ❤❤!