美文网首页程序员
vue项目中axios的封装

vue项目中axios的封装

作者: 刘淘 | 来源:发表于2020-06-23 20:37 被阅读0次

    通过上一篇文章我们了解了axios,这次结合项目中的封装,具体讲解一下。

    1 为什么要封装axios

    对axios进行封装以及将API接口按业务模块统一管理,有助于我们简化代码,方便后期维护。

    2 在项目中整体使用

    image.png

    3 逐句分析

    import axios from 'axios';
    import { Message, MessageBox } from 'element-ui';
    import store from '@/store';
    import router from '@/router';
    import { getToken } from '@/utils/auth';
    
    
    // 是否正在刷新的标记
    let isRefreshing = false;
    // 重试队列,每一项将是一个待执行的函数形式
    let requests = [];
    // 创建axios实例:service
    const service = axios.create({
      baseURL: process.env.VUE_APP_BASE_API, //设置默认的URL前缀
      withCredentials: true, // send cookies when cross-domain requests
      timeout: 60000 // 设置请求超时。如果超过了60秒,就会告知用户当前请求超时,请刷新等
    });
    service.setToken = (token) => {
      service.defaults.headers['Authorization'] = 'Bearer ' + token;
      window.localStorage.setItem('Admin-Token', token);
    };
    // 请求拦截器
    service.interceptors.request.use(
      config => {
        // do something before request is sent
        //每次请求之前判断vuex中是否存在token
        //如果存在,则统一在http请求的header上都加上token,以便后台根据token判断当前登陆情况
        //即使本地存在token,也有可能过期,所以需要对返回状态进行判断
         const token = store.getters.token;
        if token && (config.headers.Authorization = token) {
          config.headers['Authorization'] = 'Bearer ' + getToken(); 
        }
    
    
        if (['post', 'put', 'delete'].includes(config.method) && typeof config.data === 'object') {
          config.data = Object.keys(config.data).map(key => `${key}=${config.data[key]}`).join('&');
          config.data = encodeURI(config.data); // 解决参数中包含特殊字符无法保存的问题
          //设置请求头
          config.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
        }
        return config;
      },
      error => {
        // do something with request error
        console.log(error); // for debug
        return Promise.reject(error);
      }
    );
    /* 重新获取新的token*/
    function refreshToken() {
      // service是当前request.js中已创建的axios实例
      return axios({
        method: 'post',
        url: 'api-auth/oauth/token',
        params: {
          client_id: 'webApp',
          client_secret: 'webApp',
          refresh_token: localStorage.getItem('refresh_token'),
          grant_type: 'refresh_token'
        }
      }).then(res => res);
    }
    // 响应拦截器
    //在我们拿到服务器返回给我们的数据之前进行一些处理
    //主要就是进行错误的统一处理和没登录 登录过期后调整登录页的一个操作
    service.interceptors.response.use(
      /**
       * If you want to get information such as headers or status
       * Please return  response => response
       */
    
    
      /**
       * Determine the request status by custom code
       * Here is just an example
       * You can also judge the status by HTTP Status Code.
       */
      response => {
        // const res = response.data
    
    
        // if the custom code is not 20000, it is judged as an error.
        if (response.status !== 200) {
          Message({
            message: response.message || 'error',
            type: 'error',
            duration: 60 * 1000,
            showClose: true
          });
    
    
           //具体状态码需要根据项目中的实际约定的错误状态吗进行判断
           //根据返回的状态吗进行一些操作,诸如 登录过期 错误提示 长时间停留失效等
          // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
          if (response.status === 50008 || response.status === 50012 || response.status === 50014) {
            // to re-login
            MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
              confirmButtonText: 'Re-Login',
              cancelButtonText: 'Cancel',
              type: 'warning'
            }).then(() => {
              store.dispatch('user/resetToken').then(() => {
                location.reload();
              });
            });
          }
          return Promise.reject(response.message || 'error');
        } else {
          //正常拿到数据
          return response.data;
        }
      },
      error => {
        let message = error.message;
        if (error.response && error.response.data && error.response.data['resp_code']) { // 登录提示
          message = error.response.data['rsp_msg'] || error.response.data['resp_msg'];
        }
        console.log('err' + error); // for debug
    
    
        if (error.response && error.response.data && error.response.data['resp_code'] && error.response.data['resp_msg']) {
          if (error.response.data['resp_code'] === '401' && error.response.data['resp_msg'].startsWith('Invalid access token')) {
            /* 401超时*/
            const config = error.response.config;
            if (!isRefreshing) {
              isRefreshing = true;
              return refreshToken().then(res => {
                const token = res.data.access_token;
                service.setToken(token);
                config.headers['Authorization'] = 'Bearer ' + token;
                config.baseURL = '';
                // 已经刷新了token,将所有队列中的请求进行重试
                requests.forEach(cb => cb(token));
                requests = [];
                return service(config); // 重新执行当前的接口
              }).catch(res => {
                const message = '会话超时,请重新登录';
                store.dispatch('user/logout');
                router.push({ path: `/login?redirect=${message}`, params: { errorMessage: message }});
              }).finally(() => {
                isRefreshing = false;
              });
            } else {
              // 正在刷新token,将返回一个未执行resolve的promise
              return new Promise((resolve) => {
                // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
                requests.push((token) => {
                  config.baseURL = '';
                  config.headers['Authorization'] = 'Bearer ' + token;
                  resolve(service(config));
                });
              });
            }
          } else {
            Message({
              dangerouslyUseHTMLString: true,
              message: error.response.data['resp_msg'],
              type: 'error',
              duration: 5 * 1000,
              showClose: true
            });
          }
        } else {
          Message({
            dangerouslyUseHTMLString: true,
            message: `${message}${error.response && error.response.data && error.response.data.message ? '
    ' + error.response.data.message : ''}`,
            type: 'error',
            duration: 5 * 1000,
            showClose: true
          });
        }
    
    
        return Promise.reject(error);
      }
    );
    
    
    export default service;
    

    3 token

    token,一般是在登录完成之后,将用户的token通过localStorage或cookie存在本地,然后用户每次在进入页面的时候,会首先从本地存储中读取token,如果token存在说用用户已经登录过,则更新vuex中的token状态。
    在每次请求接口的时候,都会在请求的header中携带token,后台人员就可以根据携带的token判断当前登录是否过期,如果请求header中没有token,则说明没有登录过。

    参考这个更加简洁优美的axios封装 https://juejin.im/post/5b55c118f265da0f6f1aa354

    相关文章

      网友评论

        本文标题:vue项目中axios的封装

        本文链接:https://www.haomeiwen.com/subject/lskkfktx.html