记录一个自己的Axios实例,以供参考

发布时间:2024年01月05日

文章概叙

本文主要是写一下关于Axios的一些配置问题,每个人都有自己的一套标准,我现在就将自己比较常用的代码写下,也供被人做参考。

介绍

第三方包永远是最好用的,正如锅里面的永远是最香的。

Axios作为一个已经发布了n多次的包,自然不会有什么大的纰漏,所以就成为了我们最常用的请求包,而每个项目都会封装属于自己项目的一些东西,作为重中之重的Axios必须也封装一次。

创建一个Axios的实例

最基本的配置就是服务器的链接以及设置一个超时的最大时间了。

import axios from 'axios';
?
//创建axios的实例
const service = axios.create({
    // axios中请求配置有baseURL选项,表示请求URL公共部分
    baseURL: process.env.VUE_APP_BASE_API || "http://www.baidu.com",
    // 超时设置
    timeout: 10000
})


?
export default service;

设置请求头

一般来说,我们做出请求的时候,除去特别的情况,我们都想要后台返回的是json格式的字符串。

所以我们都会先去设置下header中的"content-type"为"application/json;charset=utf-8",以保证数据的格式是我们的预期

axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'

设置token

一般请款改下,我们的后台除了登录接口,其他的接口都需要一个token校验是否登录,但是由于token是在后台返回的,所以我们无法像content-type一样一开始就设置,应该在拦截器中做判断来设置。

service.interceptors.request.use(config => {
  // 是否需要设置 token
  const isToken = (config.headers || {}).isToken === false
  // 是否需要防止数据重复提交
  const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
  if (getToken() && !isToken) {
    config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
  }
  // get请求映射params参数
 
  return config
}, error => {
    console.log(error)
    Promise.reject(error)
})

上述的代码中,getToken方法是自己设置的获取token的方法,一般来说就是放在sessionStorage或者localStorage中。

需要注意的是,catch中,我们会将错误信息打印出来,个人不喜欢抛出错误,因为这儿也不需要显示什么错误了。

最主要的一点是Promise.reject出一个error,这样子就不会走到下一步

请求防抖

混前端的,防抖节流是必须掌握的,在做Api请求的时候,我们也要做下相关的防抖节流处理。而根据的依据自然是请求的API url以及它的参数,而get请求由于有幂等性,所以不需要做太多的限制。

存放上述操作的判断依据,我都喜欢放在session中,毕竟不会很大,不过为了防止数据量很大,我只会保存上一次的数据。

 if (config.method === 'get' && config.params) {
    let url = config.url + '?' + tansParams(config.params);
    url = url.slice(0, -1);
    config.params = {};
    config.url = url;
  }
  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
    const requestObj = {
      url: config.url,
      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
      time: new Date().getTime()
    }
    const sessionObj = cache.session.getJSON('sessionObj')
    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
      cache.session.setJSON('sessionObj', requestObj)
    } else {
      const s_url = sessionObj.url;                  // 请求地址
      const s_data = sessionObj.data;                // 请求数据
      const s_time = sessionObj.time;                // 请求时间
      const interval = 1000;                         // 间隔时间(ms),小于此时间视为重复提交
      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
        const message = '数据正在处理,请勿重复提交';
        console.warn(`[${s_url}]: ` + message)
        return Promise.reject(new Error(message))
      } else {
        cache.session.setJSON('sessionObj', requestObj)
      }
    }
  }

响应数据的格式化

做完了请求的的拦截之后,就需要面对response的拦截了,第一步是对状态码的判断,但是我不觉得对数据的格式化更重要,尤其是我们请求文件的时候,返回一个文件的数据,有部分人会说是一个Blob,有部分人会说是一个ArrayBuffer,部分人不清楚这些是什么…

所以我就先写这个。

顺带一提,创建一个axios的response的interceptors error的catch如下,自己看。

service.interceptors.response.use(res => {
     // 二进制数据则直接返回
    if(res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer'){
      return res.data
    }
 },
  error => {
    console.log('err' + error)
    let { message } = error;
    if (message == "Network Error") {
      message = "后端接口连接异常";
    }
    else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    }
    else if (message.includes("Request failed with status code")) {
      message = "系统接口" + message.substr(message.length - 3) + "异常";
    }
    Message({
      message: message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

401状态跳转登录页

401代表的是我们现在已经没有资格去访问后台服务了,这个时候,代表我们应该考虑下是否应该要重新登陆了(如果有登陆系统的话)。

不过这个还是得要一个开关来设置的,毕竟是凡事都有例外。

具体的如何跳转到登陆页面,要每个人的项目来设置,有可能还要传递某些参数。

service.interceptors.response.use(res => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200;
    // 获取错误信息
    const msg = errorCode[code] || res.data.msg || errorCode['default']
    // 二进制数据则直接返回
    if(res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer'){
      return res.data
    }
    if (code === 401) {
      if (!isRelogin.show) {
        isRelogin.show = true;
        MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
          confirmButtonText: '重新登录',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).then(() => {
        isRelogin.show = false;
        store.dispatch('LogOut').then(() => {
          location.href = '/index';
        })
      }).catch(() => {
        isRelogin.show = false;
      });
    }
      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
    } else if (code === 500) {
      Message({
        message: msg,
        type: 'error'
      })
      return Promise.reject(new Error(msg))
    } else if (code !== 200) {
      Notification.error({
        title: msg
      })
      return Promise.reject('error')
    } else {
      return res.data
    }
  },
  error => {
    console.log('err' + error)
    let { message } = error;
    if (message == "Network Error") {
      message = "后端接口连接异常";
    }
    else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    }
    else if (message.includes("Request failed with status code")) {
      message = "系统接口" + message.substr(message.length - 3) + "异常";
    }
    Message({
      message: message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

封装一个下载弹窗

基本上,每一个项目都会有一个下载的,所以还是得做下相关的处理,如文件请求,我们的Response格式会有很大的区别,一般都是Blob。外加一个弹窗,所以就这么简单。

// 通用下载方法
export function download(url, params, filename) {
  downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
  return service.post(url, params, {
    transformRequest: [(params) => { return tansParams(params) }],
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    responseType: 'blob'
  }).then(async (data) => {
    const isLogin = await blobValidate(data);
    if (isLogin) {
      const blob = new Blob([data])
      saveAs(blob, filename)
    } else {
      const resText = await data.text();
      const rspObj = JSON.parse(resText);
      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
      Message.error(errMsg);
    }
    downloadLoadingInstance.close();
  }).catch((r) => {
    console.error(r)
    Message.error('下载文件出现错误,请联系管理员!')
    downloadLoadingInstance.close();
  })
}

整体代码如下

import axios from 'axios' import { Notification, MessageBox, Message, Loading } from 'element-ui' import store from '@/store' import { getToken } from '@/utils/auth' import errorCode from '@/utils/errorCode' import { tansParams, blobValidate } from "@/utils/ruoyi"; import cache from '@/plugins/cache' import { saveAs } from 'file-saver' ? let downloadLoadingInstance; // 是否显示重新登录 export let isRelogin = { show: false }; ? axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' ? ? // request拦截器 service.interceptors.request.use(config => {   // 是否需要设置 token   const isToken = (config.headers || {}).isToken === false   // 是否需要防止数据重复提交   const isRepeatSubmit = (config.headers || {}).repeatSubmit === false   if (getToken() && !isToken) {
    config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改   }   // get请求映射params参数   if (config.method === 'get' && config.params) {
    let url = config.url + '?' + tansParams(config.params);
    url = url.slice(0, -1);
    config.params = {};
    config.url = url;   }   if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
    const requestObj = {
      url: config.url,
      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
      time: new Date().getTime()
    }
    const sessionObj = cache.session.getJSON('sessionObj')
    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
      cache.session.setJSON('sessionObj', requestObj)
    } else {
      const s_url = sessionObj.url;                  // 请求地址
      const s_data = sessionObj.data;                // 请求数据
      const s_time = sessionObj.time;                // 请求时间
      const interval = 1000;                         // 间隔时间(ms),小于此时间视为重复提交
      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
        const message = '数据正在处理,请勿重复提交';
        console.warn(`[${s_url}]: ` + message)
        return Promise.reject(new Error(message))
      } else {
        cache.session.setJSON('sessionObj', requestObj)
      }
    }   }   return config }, error => {
    console.log(error)
    Promise.reject(error) }) ? // 响应拦截器 service.interceptors.response.use(res => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200;
    // 获取错误信息
    const msg = errorCode[code] || res.data.msg || errorCode['default']
    // 二进制数据则直接返回
    if(res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer'){
      return res.data
    }
    if (code === 401) {
      if (!isRelogin.show) {
        isRelogin.show = true;
        MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
          confirmButtonText: '重新登录',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).then(() => {
        isRelogin.show = false;
        store.dispatch('LogOut').then(() => {
          location.href = '/index';
        })
      }).catch(() => {
        isRelogin.show = false;
      });
    }
      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
    } else if (code === 500) {
      Message({
        message: msg,
        type: 'error'
      })
      return Promise.reject(new Error(msg))
    } else if (code !== 200) {
      Notification.error({
        title: msg
      })
      return Promise.reject('error')
    } else {
      return res.data
    }   },   error => {
    console.log('err' + error)
    let { message } = error;
    if (message == "Network Error") {
      message = "后端接口连接异常";
    }
    else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    }
    else if (message.includes("Request failed with status code")) {
      message = "系统接口" + message.substr(message.length - 3) + "异常";
    }
    Message({
      message: message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)   } ) ? // 通用下载方法 export function download(url, params, filename) {   downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })   return service.post(url, params, {
    transformRequest: [(params) => { return tansParams(params) }],
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    responseType: 'blob'   }).then(async (data) => {
    const isLogin = await blobValidate(data);
    if (isLogin) {
      const blob = new Blob([data])
      saveAs(blob, filename)
    } else {
      const resText = await data.text();
      const rspObj = JSON.parse(resText);
      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
      Message.error(errMsg);
    }
    downloadLoadingInstance.close();   }).catch((r) => {
    console.error(r)
    Message.error('下载文件出现错误,请联系管理员!')
    downloadLoadingInstance.close();   }) } ? export default service ?

由于没啥还演示的,所以就不显示效果,不贴上效果图了。

在这里插入图片描述

主要介绍前端开发的博客,由衷期望各位大佬们扫码关注

文章来源:https://blog.csdn.net/HUSHILIN001/article/details/135404831
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。