Explain in detail axios encapsulation and api interface encapsulation management

I. Introduction


In fact, the main purpose of the encapsulation of axios and the unified management of the API interface is to help us simplify the code and facilitate later update and maintenance.

In the vue project, we usually use the axios library to interact with the background to obtain data, which is a promise-based http library that can run on the browser and node.js. He has many excellent features, such as intercepting requests and responses, canceling requests, converting json, client-side defense against XSRF, etc. So our Youda also decisively gave up the maintenance of its official library vue-resource, and directly recommended us to use the axios library

2. Axios packaging steps

  1. install axios
    npm install axios -S; // 安装axios复制代码

1. Directory creation

Generally, I will create a network folder in the src directory of the project as our network request module, and then create a http.js, an api.js file and a requests.js in it. The http.js file is used to encapsulate our axios, the api.js is used to manage our interface url in a unified way, and the request.js exposes the api method we placed.

// 在http.js中引入axios
import axios from 'axios'; // 引入axios
import router from '../router';
// vant的toast提示框组件,大家可根据自己的ui组件更改。
import { Toast } from 'vant'; 

  1. Environment switching

Our project environment may have development environment, test environment and production environment. We use node environment variables to match our default interface url prefix. Axios.defaults.baseURL can set the default request address of axios, so I won’t say much.

Create a config directory. Created under the directoryenv.development.js+env.production.js+env.test.js

env.development.jsThe content is as follows:

module.exports={
    baseUrl:' http://www.devele.com:4456' //开发环境用到的baseurl
}

// 环境的切换
const {baseUrl}=require('../config/env.'+process.env.NODE_ENV);
//同时 package.json的scripts需指定测试环境的模式  --mode test
 "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "test": "vue-cli-service build --mode test",
    "lint": "vue-cli-service lint"
  }
const service = axios.create({
  baseURL: baseUrl, // url = base api url + request url
  withCredentials: false, // send cookies when cross-domain requests
  timeout: 1000*12 // 请求超时
})

4. Set the request timeout as above

Set the default request timeout through axios.defaults.timeout. For example, if it exceeds 10s, the user will be notified that the current request has timed out, please refresh, etc.

  1. When setting the post request header, we need to add a request header, so we can make a default setting here, that is, set the post request header toapplication/x-www-form-urlencoded;charset=UTF-8
// 设置post请求头
service.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

  1. request interception

We can intercept a request before sending it. Why should we intercept it? What are we using to intercept the request? For example, some requests can only be accessed after the user logs in, or when posting a request, we need to serialize the data we submit. At this time, we can intercept the request before it is sent, so as to perform the operation we want.

// 先导入vuex,因为我们要使用到里面的状态对象
// vuex的路径根据自己的路径去写
import store from '@/store/index';
// 请求拦截器
service.interceptors.request.use(
  config => {
    // 不传递默认开启loading
    if (!config.hideloading) {
      // 请求是是否开启loading
      Toast.loading({
        forbidClick: true
      })
    }
      // 每次发送请求之前判断vuex中是否存在token        
        // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 
    if (store.state.token) {
      config.headers.token = store.state.token;
      //有些接口是 config.headers.Authorization = token
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

Let’s talk about token here. Generally, after the login is completed, the user’s token is stored locally through localStorage or cookie, and then every time the user enters the page (that is, in main.js), it will first read the token from the local storage. , if the token exists, indicating that the user has already logged in, update the token status in vuex. Then, every time you request the interface, the token will be carried in the header of the request, and the background staff can judge whether your login has expired based on the token you carry. If you do not carry it, it means that you have not logged in. At this time, some friends may have doubts, that is, every request carries a token, so what if a page can be accessed without user login? In fact, your front-end request can carry token, but the background can choose not to receive it!

  1. response interception
// 响应拦截器
service.interceptors.response.use(
    response => {   
        // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据     
        // 否则的话抛出错误
        if (response.status === 200) {            
            return Promise.resolve(response);        
        } else {            
            return Promise.reject(response);        
        }    
    },    
    // 服务器状态码不是2开头的的情况
    // 这里可以跟你们的后台开发人员协商好统一的错误状态码    
    // 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
    // 下面列举几个常见的操作,其他需求可自行扩展
    error => {            
        if (error.response.status) {            
            switch (error.response.status) {                
                // 401: 未登录
                // 未登录则跳转登录页面,并携带当前页面的路径
                // 在登录成功后返回当前页面,这一步需要在登录页操作。                
                case 401:                    
                    router.replace({                        
                        path: '/login',                        
                        query: { 
                            redirect: router.currentRoute.fullPath 
                        }
                    });
                    break;
                // 403 token过期
                // 登录过期对用户进行提示
                // 清除本地token和清空vuex中token对象
                // 跳转登录页面                
                case 403:
                     Toast({
                        message: '登录过期,请重新登录',
                        duration: 1000,
                        forbidClick: true
                    });
                    // 清除token
                  store.dispatch('FedLogOut').then(() => {
                    // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面 
                 router.replace({                            
                            path: '/login',                            
                            query: { 
                                redirect:router.currentRoute.fullPath 
                            }      
                  })      })       
                    break; 
                // 404请求不存在
                case 404:
                    Toast({
                        message: '网络请求不存在',
                        duration: 1500,
                        forbidClick: true
                    });
                    break;
                // 其他错误,直接抛出错误提示
                default:
                    Toast({
                        message: error.response.data.message,
                        duration: 1500,
                        forbidClick: true
                    });
            }
            return Promise.reject(error.response);
        }else {
            // 处理断网的情况
            // eg:请求超时或断网时,更新state的network状态
            // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
            // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
            store.commit('changeNetwork', false);
        }    
});
//最后导出实例
export default service;

The response interceptor is well understood, that is, the data returned to us by the server, we can do some processing on it before we get it. For example, the above idea: if the status code returned by the background is 200, then return the data normally, otherwise, make some errors we need according to the wrong status code type. In fact, here is mainly an operation of adjusting the 错误的统一处理login page 没登录.登录过期

At this point, the encapsulation of axios is basically completed, let's briefly talk about the unified management of api

3. Unified management of api interface

Created a new api folder, which contains an index.js, and multiple interface js files divided according to modules. index.js is an api export, and other js is used to manage the interface of each module.

For example the following article.js:

/**
 * article模块接口列表
 */
import request from '@/network/http'; // 导入http中创建的axios实例
import qs from 'qs'; // 根据需求是否导入qs模块 
const article = {    
    // 新闻列表    
    articleList () {        
       return request({
       url: '/artical',
       method: 'get',
       params,
       hideloading: false //设置不隐藏加载loading
    })  
    },    
    // 新闻详情,演示    
    articleDetail (id, params) {        
         return request({
		      url: '/detail',
		      method: 'get',
		      params:{
		        goodsId
		      },
		      hideloading: true
		    })
    },
    // post提交    
    login (data) {        
      return request({
      url:'/adduser',
      method:'post',
      data:qs.stringify(data), //注意post提交用data参数
      hideloading: true
     })   
    }
    // 其他接口…………
}
export default article;

index.js code:

/** 
 * api接口的统一出口
 */
// 文章模块接口
import article from '@/api/article';
// 其他模块的接口…… 
// 导出接口
export default {    
    article,
    // ……
}

Use in components (import on demand)

import {article} from '@/api/index'
created(){
   article.articleList().then(info=>{
       if(info.code==200)
     this.num=info.data
  }
     })
}

The api is mounted on vue.prototype to save the introduction steps

In order to facilitate the calling of api, we need to mount it on the prototype of vue. In main.js:

import Vue from 'vue'
import App from './App'
import router from './router' // 导入路由文件
import store from './store' // 导入vuex文件
import api from './api' // 导入api接口
Vue.prototype.$api = api; // 将api挂载到vue的原型上复制代码

Then we can use it like this in the component

//无需导入
methods: {    
    onLoad(id) {      
        this.$api.article.articleDetail(id, {        
            api: 123      
        }).then(res=> {
            // 执行某些操作      
        })    
    }  
}


Handling of network disconnection

Add the following app.vue

<template>  
    <div id="app">    
        <div v-if="!network">      
            <h3>我没网了</h3>      
            <div @click="onRefresh">刷新</div>      
        </div>    
        <router-view/>      
    </div>
</template>
<script>
    import { mapState } from 'vuex';
    export default {  
        name: 'App',  
        computed: {    
            ...mapState(['network'])  
        },  
        methods: {    
            // 通过跳转一个空页面再返回的方式来实现刷新当前页面数据的目的
            onRefresh () {      
                this.$router.replace('/refresh')    
            }  
        }
    }
</script>

This is app.vue, here is a brief demonstration of disconnection. Introduced 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 disconnected component is loaded, and the component of the corresponding page is not loaded. When clicking refresh, we realize the operation of re-acquiring data by jumping to the refesh page and then returning immediately. So we need to create a new refresh.vue page and return to the current page in its beforeRouteEnter hook.

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

Guess you like

Origin blog.csdn.net/onebound_linda/article/details/131001264