Axios package and api interface management

I. Introduction

The packaging of axios and the unified management of the api interface are actually the main purpose of helping us simplify the code and facilitate the 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 in the browser and node.js. It has many excellent features, such as intercepting requests and responses, canceling requests, converting json, and defending against XSRF on the client side. So our Yuda also decisively gave up the maintenance of its official library vue-resource, and directly recommended us to use the axios library

Two, axios packaging steps

  1. Install axios
    npm install axios; // 安装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 and an api.js file and a reques.js in it. The http.js file is used to encapsulate our axios, api.js is used to manage our interface url uniformly, and request.js exposes the api method we put in externally.

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


  1. Environmental switching

Our project environment may include 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, not much to say.

Create the config directory.
The contents created under the directory env.development.js+env.production.js+env.test.js
env.development.jsare 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 // 请求超时
})

  1. Set request timeout as above

Set the default request timeout time through axios.defaults.timeout. For example, after 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 request header of the post 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 the request. Why do we intercept it? What do we intercept the request for? For example, some requests need to be accessed after the user logs in, or when post requests, we need to serialize the data we submit. At this time, we can perform an interception before the request is sent 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)
  }
)

Here to talk about the token, 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), the token is first read from the local storage , If the token exists, indicating that the user has logged in, update the token status in vuex. Then, every time the interface is requested, the token will be carried in the header of the request, and the background personnel can judge whether your login has expired based on the token you carry. If you do not carry it, it means you have not logged in. At this time, some friends may have questions, that is, each request carries a token. What if a page can be accessed without the user's login? In fact, your front-end request can carry token, but the back-end 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 that the server returns to us, we can do some processing on it before we get it. For example, the above thought: If the status code returned by the background is 200, the data is returned normally, otherwise, we need to make some errors according to the wrong status code type. In fact, this is mainly an operation of performing 错误的统一处理and 没登录or 登录过期adjusting the login page

Three, api interface unified management

A new api folder is created, which contains an index.js and multiple interface js files divided by modules. index.js is an api export, and other js is used to manage the interfaces 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
  }
     })
}

Mount the api to vue.prototype to save the introduction step.
In order to facilitate the call of the api, we need to mount it to 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 in the component

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

Handling of 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. It is introduced in http.js that we will update the state of the network in vue when the network is disconnected, so here we judge whether we need to load this disconnected component according to the state of the network. In the case of network disconnection, the disconnection component is loaded, but the component of the corresponding page is not loaded. When you click refresh, we realize the operation of reacquiring data by jumping to the refesh page and then returning immediately. Therefore, we need to create a 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/zxlong020/article/details/108588820