axios二次封装和使用

    

axios二次封装和使用

在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(...)

所有原创文章采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。
本站部分内容收集于互联网,如果有侵权内容、不妥之处,请联系我们删除。敬请谅解!

添加新评论

  关于博主

QQ:1960727927
E-Mail:ceet@vip.qq.com
个人主页:https://tencent.信息

  近期评论

  •  osc_omoo: 前端加密后端解密吧?你不会用的是nodejs吧?改成java的后端解密麻烦吗?
  •  环洋诚信SSL证书客服: 按照博主的思路和代码,我已经搭建成功了。但是有一个小小的问题想请教下,重新执行安装代码后,之前...
  •  澄弘: 已经添加了你的博客

一段未来,一段现在,一段过去。

把我对你的好全部记着,然后原封不动的还给我。

时光总是带走了最美好的东西。

有心才会累,没心无所谓。

也许在我心里有太多你的痕迹,以至于后来我爱的人都像你。

有的时候我真的好想抛下一切,消失一整天。