当前位置:   article > 正文

axios 的二次封装(拦截重复请求、异常统一处理)

axios 的二次封装(拦截重复请求、异常统一处理)

一直想封装一下 axios, 可以方便项目中使用,今天有时间,就好好研究了一下。


源码:

  1. // util/axios.js
  2. import axios from 'axios'
  3. const pending = {}
  4. const CancelToken = axios.CancelToken
  5. const removePending = (key, isRequest = false) => {
  6. if (pending[key] && isRequest) {
  7. pending[key]('取消重复请求')
  8. }
  9. delete pending[key]
  10. }
  11. const getRequestIdentify = (config, isReuest = false) => {
  12. let url = config.url
  13. if (isReuest) {
  14. url = config.baseURL + config.url.substring(1, config.url.length)
  15. }
  16. return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data))
  17. }
  18. // 请求拦截器
  19. axios.interceptors.request.use(config => {
  20. // 拦截重复请求(即当前正在进行的相同请求)
  21. let requestData = getRequestIdentify(config, true)
  22. removePending(requestData, true)
  23. config.cancelToken = new CancelToken((c) => {
  24. pending[requestData] = c
  25. })
  26. return config
  27. }, error => {
  28. return Promise.reject(error)
  29. })
  30. // 异常处理
  31. axios.interceptors.response.use(response => {
  32. // 把已经完成的请求从 pending 中移除
  33. let requestData = getRequestIdentify(response.config)
  34. removePending(requestData)
  35. return {
  36. code: response.status,
  37. message: response.statusText,
  38. data: response.data
  39. }
  40. }, err => {
  41. if (err && err.response) {
  42. switch (err.response.status) {
  43. case 400:
  44. err.message = '错误请求'
  45. break
  46. case 401:
  47. err.message = '未授权,请重新登录'
  48. break
  49. case 403:
  50. err.message = '拒绝访问'
  51. break
  52. case 404:
  53. err.message = '请求错误,未找到该资源'
  54. break
  55. case 405:
  56. err.message = '请求方法未允许'
  57. break
  58. case 408:
  59. err.message = '请求超时'
  60. break
  61. case 500:
  62. err.message = '服务器端出错'
  63. break
  64. case 501:
  65. err.message = '网络未实现'
  66. break
  67. case 502:
  68. err.message = '网络错误'
  69. break
  70. case 503:
  71. err.message = '服务不可用'
  72. break
  73. case 504:
  74. err.message = '网络超时'
  75. break
  76. case 505:
  77. err.message = 'http版本不支持该请求'
  78. break
  79. default:
  80. err.message = `连接错误${err.response.status}`
  81. }
  82. let errData = {
  83. code: err.response.status,
  84. message: err.message
  85. }
  86. // 统一错误处理可以放这,例如页面提示错误...
  87. console.log('统一错误处理: ', errData)
  88. }
  89. return Promise.reject(err)
  90. })
  91. axios.defaults.baseURL = 'http://localhost:3000/'
  92. export default (instance) => {
  93. instance.prototype.axios = (data) => {
  94. var _params = {
  95. method: !data.method ? 'get' : data.method.toLowerCase(),
  96. url: data.url
  97. }
  98. if (_params.method === 'get') {
  99. _params.params = data.params || {}
  100. } else {
  101. _params.data = data.params || {}
  102. }
  103. return new Promise((resolve, reject) => {
  104. axios(_params).then(response => {
  105. resolve(response)
  106. }).catch(error => {
  107. reject(error)
  108. })
  109. })
  110. }
  111. }

调用:

  1. // main.js
  2. import axios from './util/axios'
  3. Vue.use(axios)
  1. // HelloWorld.vue
  2. methods: {
  3. getUserInfo (_id) {
  4. this.axios({
  5. url: '/users',
  6. method: 'get',
  7. params: { 'id': _id }
  8. }).then(response => {
  9. console.log(response)
  10. })
  11. }
  12. }

说明:

全局的 axios 默认值

本人使用 json-server 搭建 mock 服务(这个,有必要的话,之后会写一下),服务器地址为http://localhost:3000/,所以设置 axios 的 基础URL路径设置为http://localhost:3000/

另外,大家有需要的话,也可以对 axios.defaults.headers 默认的请求头、axios.defaults.timeout请求超时时间 进行设置。这里就不设置了。

axios.defaults.baseURL = 'http://localhost:3000/'

get、post请求的封装

这里,get、post 请求具体调用的时候,都通过 this.axios(requestData)来调用,其中 requestData有统一的格式,如下

  1. const requestData = {
  2. url: '/users', // 必填
  3. method: 'get', // 选填,默认 'get'
  4. params: {} // 选填,默认 {}
  5. }

这部分通过 requestData.method处理 axios发送请求时,requestData.params 是赋值给 _params.params(get 请求传递参数) 还是赋值给 _params.data(post 请求传递参数)。

  1. export default (instance) => {
  2. instance.prototype.axios = (data) => {
  3. var _params = {
  4. method: !data.method ? 'get' : data.method.toLowerCase(),
  5. url: data.url
  6. }
  7. if (_params.method === 'get') {
  8. _params.params = data.params || {}
  9. } else {
  10. _params.data = data.params || {}
  11. }
  12. return new Promise((resolve, reject) => {
  13. axios(_params).then(response => {
  14. resolve(response)
  15. }).catch(error => {
  16. reject(error)
  17. })
  18. })
  19. }
  20. }

拦截重复请求

如何标识每个请求

下面函数,通过一个请求参数中的 url, params(get 请求传递参数)或 data(post 请求传递参数)来表示每一个请求。

使用请求路径加请求参数的标识方式,避免了相同请求路径,不同请求参数的情况下的错误拦截。

其中需要注意的地方是,请求拦截器中 config.url = '/users', 响应拦截器中 config.url = 'http://localhost:3000/users',所以加上一个标识isReuest来计算请求的全路径

  1. /**
  2. * config: 请求数据
  3. * isReuest: 请求拦截器中 config.url = '/users', 响应拦截器中 config.url = 'http://localhost:3000/users',所以加上一个标识来计算请求的全路径
  4. */
  5. const getRequestIdentify = (config, isReuest = false) => {
  6. let url = config.url
  7. if (isReuest) {
  8. url = config.baseURL + config.url.substring(1, config.url.length)
  9. }
  10. return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data))
  11. }
请求拦截器

使用 cancel token 取消请求。

这里每个请求通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token

  1. // 添加请求拦截器
  2. axios.interceptors.request.use(config => {
  3. // 发送请求之前,拦截重复请求(即当前正在进行的相同请求)
  4. let requestData = getRequestIdentify(config, true)
  5. removePending(requestData, true)
  6. config.cancelToken = new CancelToken((c) => {
  7. pending[requestData] = c
  8. })
  9. return config
  10. }, error => {
  11. return Promise.reject(error)
  12. })
取消请求

这一步是结合上面的请求拦截器执行的,取消重复请求的同时删除记录,并且在下面的响应拦截器也会调用该函数,即完成请求后删除请求记录。

  1. // key: 请求标识;isRequest 完成请求后也需要执行删除记录,所以添加此参数避免执行无用操作
  2. const removePending = (key, isRequest = false) => {
  3. if (pending[key] && isRequest) {
  4. pending[key]('取消重复请求')
  5. }
  6. delete pending[key] // 把这条记录从 pending 中移除
  7. }

请求异常处理

可以使用响应拦截器来统一处理请求异常,例如,统一返回的数据的格式、统一处理异常报错...

  1. // 异常处理
  2. axios.interceptors.response.use(response => {
  3. // 把已经完成的请求从 pending 中移除
  4. let requestData = getRequestIdentify(response.config)
  5. removePending(requestData)
  6. return {
  7. code: response.status,
  8. message: response.statusText,
  9. data: response.data
  10. }
  11. }, err => {
  12. if (err && err.response) {
  13. switch (err.response.status) {
  14. case 400:
  15. err.message = '错误请求'
  16. break
  17. case 401:
  18. err.message = '未授权,请重新登录'
  19. break
  20. case 403:
  21. err.message = '拒绝访问'
  22. break
  23. case 404:
  24. err.message = '请求错误,未找到该资源'
  25. break
  26. case 405:
  27. err.message = '请求方法未允许'
  28. break
  29. case 408:
  30. err.message = '请求超时'
  31. break
  32. case 500:
  33. err.message = '服务器端出错'
  34. break
  35. case 501:
  36. err.message = '网络未实现'
  37. break
  38. case 502:
  39. err.message = '网络错误'
  40. break
  41. case 503:
  42. err.message = '服务不可用'
  43. break
  44. case 504:
  45. err.message = '网络超时'
  46. break
  47. case 505:
  48. err.message = 'http版本不支持该请求'
  49. break
  50. default:
  51. err.message = `连接错误${err.response.status}`
  52. }
  53. let errData = {
  54. code: err.response.status,
  55. message: err.message
  56. }
  57. // 统一错误处理可以放这,例如页面提示错误...
  58. console.log('统一错误处理: ', errData)
  59. }
  60. return Promise.reject(err)
  61. })

疑问:

CancelToken 这一部分,小女子还不是很清楚原理,希望大家指导一下~~~


参考地址:

axios 的 github 地址
https://segmentfault.com/a/11...
https://www.jianshu.com/p/444...

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/57068?site
推荐阅读
相关标签
  

闽ICP备14008679号