1、请求封装

封装代码:

/**axios封装
 * 请求拦截、相应拦截、错误统一处理
 */
import axios from 'axios'
import QS from 'qs';
import { Message } from 'element-ui'
import router from "@/router"

function signout() {
  localStorage.removeItem('token')
  router.replace({
    path: "/",
  });
}

// 环境的切换
if (process.env.NODE_ENV == 'development') {
  axios.defaults.baseURL = '/Api'
} else if (process.env.NODE_ENV == 'production') {
  axios.defaults.baseURL = '/pfps'
}

// 请求超时时间
axios.defaults.timeout = 15000
// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'

// 请求拦截器
axios.interceptors.request.use(
  config => {
    // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
    const token = localStorage.getItem('token')
    token ? (config.headers.Token = token) : null
    config.headers['route'] = localStorage.getItem('route')
    return config
  },
  error => {
    return Promise.error(error)
  }
)

// 响应拦截器
axios.interceptors.response.use(
  response => {
    if (response.status === 200) {
      //更新token
      if (response.headers.token) {
        localStorage.setItem('token', response.headers.token)
      }
      return Promise.resolve(response)
    } else {
      return Promise.reject(response)
    }
  },
  //服务器状态码不是200的情况
  error => {
    if (error.response.status) {
      switch (error.response.status) {
        // 401: 未登录
        case 401:
          Message({
            showClose: true,
            message: '未登录',
          })
          signout()
          break
        // 403 token过期
        // 登录过期对用户进行提示
        // 清除本地token对象
        // 跳转登录页面
        case 403:
          Message({
            showClose: true,
            message: '登录过期,请重新登录',
          })
          // 清除token
          signout()
          break
        // 404请求不存在
        case 404:
          Message({
            showClose: true,
            message: '网络请求不存在',
          })
          break
        case 500:
          Message({
            showClose: true,
            message: 'Request failed with status code 500',
          })
          break
        // 其他错误,直接抛出错误提示
        default:
          console.log(error)
          Message({
            showClose: true,
            message: error.response.data.message,
          })
      }
      return Promise.reject(error.response)
    } else {
      console.log(error)
      Message({
        showClose: true,
        message: '服务器异常!',
      })
    }
  }
)
/**
 * get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
export function get(url, params, type) {
  return new Promise((resolve, reject) => {
    axios
      .get(url, {
        params: params,
        responseType: type,
      })
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err.data)
      })
  })
}
/** 
 * post方法,对应post 序列化请求
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
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)
          })
  });
}
/**
 * post方法,对应post请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
export function postNormal(url, params) {
  return new Promise((resolve, reject) => {
    axios
      .post(url, params)
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err.data)
      })
  })
}

/**
 * 文件上传
 */
export function postFile(url, params) {
  return new Promise((resolve, reject) => {
    axios
      .post(url, params, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      })
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err.data)
      })
  })
}

/**
 * 获取json
 */
export const getJson = function (method) {
  return new Promise((resolve, reject) => {
    axios({
      method: 'get',
      url: method,
      dataType: 'json',
      crossDomain: true,
      cache: false,
    })
      .then(res => {
        resolve(res.data)
      })
      .catch(error => {
        reject(error.data)
      })
  })
}

菜鸟感觉这个封装挺好的,可以注释掉下面提到的那一行,然后自己传参设置!

这里菜鸟更推荐另外的封装方式,更简洁明了:vue3+eslint(推荐配置)+prettier+webpack+elementplus+国际化+axios封装+pinia

2、问题

菜鸟在和后端交互时,都是使用的postNormal,但是在axios 0.21版本左右时,发现传递给后端的就是json,但是在aioxs 1.2版本左右时,发现传递给后端的就是form-data。

菜鸟就很不理解,明明我们除了传递文件时会用到new FormData、‘Content-Type’: ‘multipart/form-data’,然后将要传递的值append进去FormData之外,其他全是传递的对象,浏览器是为什么会把对象一下理解成JSON、一下理解成FormData的呢?

百思不得其解的菜鸟和导师一起对比代码,发现真的除了版本,基本上没有区别,所以直接看源码:

axios 0.21源码

直接查看axios 0.21的源码,找到node_modules\axios\lib\defaults.js,查看代码
在这里插入图片描述
我们传递的一般都是对象,所以看这个isObject函数,发现里面调用了setContentTypeIfUnset
在这里插入图片描述
发现其实setContentTypeIfUnset就是设置请求头,所以可以看出axios默认请求头就是application/json;charset=utf-8

这时候有人就会问,我不是在最上面写了一段这个话吗?

// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'

但是其实只要你不是传参传过去的,那么经过axios复杂的判断后,大概率设置是无效的!!!只有传参才是yyds,大神建议也是不要直接设置,最好是你哪里要用就传参改,类似封装的postFile!!!

axios 1.2源码

直接查看axios 1.2源码,找到node_modules\axios\lib\defaults\index.js,查看代码
在这里插入图片描述
在这里插入图片描述
和上面一样看看这个isObject函数,里面的toURLEncodedForm也是设置请求头,但是不一样的是这个会判断你设置请求头没有,要是设置了,就直接按照你设置的返回
在这里插入图片描述
然后application/x-www-form-urlencoded;charset=utf-8 默认就是form-data的数据,所以产生了菜鸟遇见的问题!!!

总结

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐