axios二次封装和使用

作者: 黄孝斌 | 来源:发表于2019-02-24 01:06 被阅读97次

    前言

    在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御XSRF等。所以我们的尤大大也是果断放弃了对其官方库vue-resource的维护,直接推荐我们使用axios库。如果还对axios不了解的,可以移步axios文档。

    网上也很多对axios进行二次封装的,但我觉得大多都不够通用或者写得不够全面,所以在借鉴大家的封装经验,我进行补充,形成我觉得完善的封装,当然还可以在此之上不断扩展,没有最好只有更好。

    二次封装的目的

    • 便于维护:对于一个项目来说,代码一定要利于维护,当细节发生变化时,可修改接口内部实现,接口使用方无需配套修改。

    • 统一实现:封装底层实现,形成项目特有的api实现,可统一和规范项目中的代码实现,避免天花乱坠的实现,从而难以维护。

    • 程序复用:封装时,需充分考虑程序的可复用性,通过提供接口api的形式让使用者来实现细节。

    二次封装的效果

    • 将axios封装成Vue插件使用

    • 统一api请求(基于Vue全局、基于单个Vue实例)

    • 统一axios默认值配置方式

    • 暴露接口统一由使用方配置请求、响应拦截器

    • 暴露接口统一由使用方配置请求、响应拦截器

    • 内部默认实现重复请求拦截的拦截器

    封装实现

    
    import axios from 'axios'
    
    import qs from 'qs'
    
    import $ from 'jquery'
    
    axios.defaults.timeout = 10000
    
    axios.defaults.baseURL = '' // 填写域名,需以/结尾,后续在拼装URL时使用
    
    axios.defaults.withCredentials = false // 表示跨域请求时是否需要使用凭证
    
    // ------------------------------私有内部方法-------------------------------
    
    // axios 拦截重复请求
    
    const pending = {}
    
    const CancelToken = axios.CancelToken
    
    const removePending = (key, isRequest = false) => {
    
      if (pending[key] && isRequest) {
    
        pending[key]('取消重复请求') // 执行CancelToken,用于取消进行中的请求
    
      }
    
      delete pending[key]
    
    }
    
    const getRequestIdentify = (config, isReuest = false) => {
    
      // request的时候,config.url是action路径;response的时候,config.url是全路径。所以存在判断请求操作从而处理url
    
      let url = config.url
    
      if (isReuest) {
    
        url = config.baseURL + config.url.substring(1, config.url.length)
    
      }
    
      return config.method === 'get' || config.method === 'delete' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(url + JSON.stringify(config.data))
    
    }
    
    /**
    
    * 封装get post delete put方法
    
    * @param method
    
    * @param url
    
    * @param data
    
    * @param selfConfig 单个请求的个性化配置
    
    * @returns {Promise}
    
    */
    
    function methodAxios (method, url, params, selfConfig = {}) {
    
      let httpDefault = {
    
        method: method,
    
        url: url,
    
        // `params` 是即将与请求一起发送的 URL 参数
    
        // `data` 是作为请求主体被发送的数据
    
        params: method === 'GET' || method === 'DELETE' ? params : null,
    
        data: method === 'POST' || method === 'PUT' ? qs.stringify(params) : null
    
      }
    
      let requestConfig = $.extend({}, httpDefault, selfConfig)
    
      // 这个其实可以直接return axios(requestConfig),为何需要再增加一层Promise?
    
      // 这里是有原因的:
    
      // 1、如果直接return axios(requestConfig),请求成功或失败的处理是交由使用者
    
      // 2、这里封装多一层Promise,是便于此处封装时考虑添加公共处理如开启遮罩层关闭遮罩层,之后才抛出调用结果给调用方,而不应该由调用方赖关闭遮罩层
    
      return new Promise((resolve, reject) => {
    
        axios(requestConfig)
    
          .then((response) => {
    
            resolve(response)
    
          }).catch((error) => {
    
            reject(error)
    
          })
    
      })
    
    }
    
    /**
    
    * 封装添加axios All
    
    * @param promiseArray
    
    * @returns {Promise}
    
    */
    
    function allAxios (promiseArray) {
    
      // 这个其实可以直接axios.all(promiseArray),为何需要再增加一层Promise?
    
      // 这里是有原因的:
    
      // 1、如果直接axios.all(promiseArray),请求成功或失败的处理是交由使用者
    
      // 2、这里封装多一层Promise,是便于此处封装时考虑添加公共处理如开启遮罩层关闭遮罩层,之后才抛出调用结果给调用方,而不应该由调用方赖关闭遮罩层
    
      return new Promise((resolve, reject) => {
    
        axios.all(promiseArray)
    
          .then(allResponse => {
    
            resolve(allResponse)
    
          })
    
          .catch((error) => {
    
            reject(error)
    
          })
    
      })
    
    }
    
    /**
    
    * 封装添加请求拦截器方法
    
    * @param interceptorFun
    
    * @param errorFun
    
    * @returns {Interceptor}
    
    */
    
    function setRequestInterceptor (interceptorFun, errorFun) {
    
      return axios.interceptors.request.use(interceptorFun, errorFun)
    
    }
    
    /**
    
    * 封装卸载请求拦截器方法
    
    * @param interceptor
    
    * @returns {Interceptor}
    
    */
    
    function ejectRequestInterceptor (interceptor) {
    
      return axios.interceptors.request.eject(interceptor)
    
    }
    
    /**
    
    * 封装添加响应拦截器方法
    
    * @param interceptorFun
    
    * @param errorFun
    
    * @returns {Interceptor}
    
    */
    
    function setResponseInterceptor (interceptorFun, errorFun) {
    
      return axios.interceptors.response.use(interceptorFun, errorFun)
    
    }
    
    /**
    
    * 封装卸载响应拦截器方法
    
    * @param interceptor
    
    * @returns {Interceptor}
    
    */
    
    function ejectResponseInterceptor (interceptor) {
    
      return axios.interceptors.response.eject(interceptor)
    
    }
    
    /**
    
    * 封装设置axios默认参数值方法
    
    * @param key 需要是在默认值列表中key
    
    * @param value
    
    */
    
    function setDefaultValue (key, value) {
    
      axios.defaults[key] = value
    
    }
    
    /**
    
    * 封装设置axios默认参数值方法
    
    * @param key 需要是在默认值列表中key
    
    */
    
    function setDefaultValues (defaultValues) {
    
      for (let key in defaultValues) {
    
        axios.defaults[key] = defaultValues[key]
    
      }
    
    }
    
    // -------------------------------基础公共配置----------------------
    
    // 请求拦截器--公共
    
    // 1、headers添加Content-Type
    
    // 2、拦截重复请求
    
    setRequestInterceptor(
    
      config => {
    
        config.headers = {
    
          'Content-Type': 'application/x-www-form-urlencoded',
    
          'X-Requested-With': 'XMLHttpRequest'
    
        }
    
        // 拦截重复请求(即当前正在进行的相同请求)
    
        let requestData = getRequestIdentify(config, true)
    
        removePending(requestData, true)
    
        config.cancelToken = new CancelToken((c) => {
    
          pending[requestData] = c
    
        })
    
        return config
    
      },
    
      error => {
    
        // Do something with request error
    
        return Promise.reject(error)
    
      }
    
    )
    
    // 响应拦截器--公共
    
    // 1、拦截重复请求
    
    // 2、对响应结果进行判断--请求状态
    
    setResponseInterceptor(
    
      response => {
    
      // 把已经完成的请求从 pending 中移除
    
        let requestData = getRequestIdentify(response.config)
    
        removePending(requestData)
    
        return response
    
      }, error => {
    
        if (error && error.response) {
    
          switch (error.response.status) {
    
            case 400:
    
              error.message = '错误请求'
    
              break
    
            case 401:
    
              error.message = '未授权,请重新登录'
    
              break
    
            case 403:
    
              error.message = '拒绝访问'
    
              break
    
            case 404:
    
              error.message = '请求错误,未找到该资源'
    
              break
    
            case 405:
    
              error.message = '请求方法未允许'
    
              break
    
            case 408:
    
              error.message = '请求超时'
    
              break
    
            case 500:
    
              error.message = '服务器端出错'
    
              break
    
            case 501:
    
              error.message = '网络未实现'
    
              break
    
            case 502:
    
              error.message = '网络错误'
    
              break
    
            case 503:
    
              error.message = '服务不可用'
    
              break
    
            case 504:
    
              error.message = '网络超时'
    
              break
    
            case 505:
    
              error.message = 'http版本不支持该请求'
    
              break
    
            default:
    
              error.message = `连接错误${error.response.status}`
    
          }
    
        } else {
    
          if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) { // 请求超时
    
            console.log('连接请求超时')
    
          }
    
        }
    
        return Promise.reject(error)
    
      }
    
    )
    
    // 输出函数getAxios、postAxios、putAxios、delectAxios,供其他文件调用-----------------------------
    
    // Vue.js的插件应当有一个公开方法 install。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。
    
    // 封装给vue的this.$faceAxios和Vue.faceAxios使用的公共对象方法集合
    
    const faceAxiosObject = {
    
      get: (url, params, selfConfig) => methodAxios('GET', url, params, selfConfig),
    
      post: (url, params, selfConfig) => methodAxios('POST', url, params, selfConfig),
    
      put: (url, params, selfConfig) => methodAxios('PUT', url, params, selfConfig),
    
      delete: (url, params, selfConfig) => methodAxios('DELETE', url, params, selfConfig),
    
      all: (promiseArr) => allAxios(promiseArr)
    
    }
    
    // 用于Vue安装的对象
    
    const install = Vue => {
    
      if (install.installed) {
    
        return
    
      }
    
      install.installed = true
    
      // 基于全局Vue对象使用faceAxios
    
      Vue.faceAxios = faceAxiosObject
    
      // 在一个Vue实例内使用$faceAxios
    
      Object.defineProperties(Vue.prototype, {
    
        $faceAxios: {
    
          get () {
    
            return faceAxiosObject
    
          }
    
        }
    
      })
    
    }
    
    export default {
    
      install,
    
      // 基于全局的公共方法
    
      setRequestInterceptor: (interceptorFun, errorFun) => setRequestInterceptor(interceptorFun, errorFun),
    
      ejectRequestInterceptor: (interceptor) => ejectRequestInterceptor(interceptor),
    
      setResponseInterceptor: (interceptorFun, errorFun) => setResponseInterceptor(interceptorFun, errorFun),
    
      ejectResponseInterceptor: (interceptor) => ejectResponseInterceptor(interceptor),
    
      setDefaultValue: (key, value) => setDefaultValue(key, value),
    
      setDefaultValues: (defaultValues) => setDefaultValues(defaultValues)
    
    }
    
    

    封装使用

    import Vue from 'vue'
    import FaceAxios from '@/faceAxios/api' // 我是存放在src的faceAxios目录
    Vue.use(FaceAxios)
    //全局使用
    FaceAxios.setDefaultValue('baseURL', 'http://localhost:8080/') // 设置公共参数
    FaceAxios.setDefaultValues({
      'baseURL': 'http://localhost:8080/'
    })
    FaceAxios.setRequestInterceptor() // 设置拦截器
    // 基于Vue的全局使用
    Vue.faceAxios.post('/queryList', {id: '123'}, {timeout: 5000}).then(...)
    //基于单个Vue实例使用
    this.$faceAxios.post('/queryList', {id: '123'}, {timeout: 5000}).then(...)
    

    本人微信:iamhxb 有疑问或交流可添加好友联系

    IMG_1147.JPG

    相关文章

      网友评论

        本文标题:axios二次封装和使用

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