axios 的二次封裝(攔截重複請求、異常統一處理)

一直想封裝一下 axios, 能夠方便項目中使用,今天有時間,就好好研究了一下。vue


源碼:

// util/axios.js
import axios from 'axios'

const pending = {}
const CancelToken = axios.CancelToken
const removePending = (key, isRequest = false) => {
  if (pending[key] && isRequest) {
    pending[key]('取消重複請求')
  }
  delete pending[key]
}
const getRequestIdentify = (config, isReuest = false) => {
  let url = config.url
  if (isReuest) {
    url = config.baseURL + config.url.substring(1, config.url.length)
  }
  return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data))
}

// 請求攔截器
axios.interceptors.request.use(config => {
  // 攔截重複請求(即當前正在進行的相同請求)
  let requestData = getRequestIdentify(config, true)
  removePending(requestData, true)

  config.cancelToken = new CancelToken((c) => {
    pending[requestData] = c
  })

  return config
}, error => {
  return Promise.reject(error)
})

// 異常處理
axios.interceptors.response.use(response => {
  // 把已經完成的請求從 pending 中移除
  let requestData = getRequestIdentify(response.config)
  removePending(requestData)

  return {
    code: response.status,
    message: response.statusText,
    data: response.data
  }
}, err => {
  if (err && err.response) {
    switch (err.response.status) {
      case 400:
        err.message = '錯誤請求'
        break
      case 401:
        err.message = '未受權,請從新登陸'
        break
      case 403:
        err.message = '拒絕訪問'
        break
      case 404:
        err.message = '請求錯誤,未找到該資源'
        break
      case 405:
        err.message = '請求方法未容許'
        break
      case 408:
        err.message = '請求超時'
        break
      case 500:
        err.message = '服務器端出錯'
        break
      case 501:
        err.message = '網絡未實現'
        break
      case 502:
        err.message = '網絡錯誤'
        break
      case 503:
        err.message = '服務不可用'
        break
      case 504:
        err.message = '網絡超時'
        break
      case 505:
        err.message = 'http版本不支持該請求'
        break
      default:
        err.message = `鏈接錯誤${err.response.status}`
    }
    let errData = {
      code: err.response.status,
      message: err.message
    }
    // 統一錯誤處理能夠放這,例如頁面提示錯誤...
    console.log('統一錯誤處理: ', errData)
  }

  return Promise.reject(err)
})

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

export default (instance) => {
  instance.prototype.axios = (data) => {
    var _params = {
      method: !data.method ? 'get' : data.method.toLowerCase(),
      url: data.url
    }
    if (_params.method === 'get') {
      _params.params = data.params || {}
    } else {
      _params.data = data.params || {}
    }

    return new Promise((resolve, reject) => {
      axios(_params).then(response => {
        resolve(response)
      }).catch(error => {
        reject(error)
      })
    })
  }
}

調用:

// main.js
import axios from './util/axios'

Vue.use(axios)
// HelloWorld.vue
  methods: {
    getUserInfo (_id) {
      this.axios({
        url: '/users',
        method: 'get',
        params: { 'id': _id }
      }).then(response => {
        console.log(response)
      })
    }
  }

說明:

全局的 axios 默認值

本人使用 json-server 搭建 mock 服務(這個,有必要的話,以後會寫一下),服務器地址爲http://localhost:3000/,因此設置 axios 的 基礎URL路徑設置爲http://localhost:3000/ios

另外,你們有須要的話,也能夠對 axios.defaults.headers 默認的請求頭、axios.defaults.timeout請求超時時間 進行設置。這裏就不設置了。git

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

get、post請求的封裝

這裏,get、post 請求具體調用的時候,都經過 this.axios(requestData)來調用,其中 requestData有統一的格式,以下github

const requestData = {
  url: '/users', // 必填
  method: 'get', // 選填,默認 'get'
  params: {} // 選填,默認 {}
}

這部分經過 requestData.method處理 axios發送請求時,requestData.params 是賦值給 _params.params(get 請求傳遞參數) 仍是賦值給 _params.data(post 請求傳遞參數)。json

export default (instance) => {
  instance.prototype.axios = (data) => {
    var _params = {
      method: !data.method ? 'get' : data.method.toLowerCase(),
      url: data.url
    }
    if (_params.method === 'get') {
      _params.params = data.params || {}
    } else {
      _params.data = data.params || {}
    }

    return new Promise((resolve, reject) => {
      axios(_params).then(response => {
        resolve(response)
      }).catch(error => {
        reject(error)
      })
    })
  }
}

攔截重複請求

如何標識每一個請求

下面函數,經過一個請求參數中的 url, params(get 請求傳遞參數)或 data(post 請求傳遞參數)來表示每個請求。axios

使用請求路徑加請求參數的標識方式,避免了相同請求路徑,不一樣請求參數的狀況下的錯誤攔截。segmentfault

其中須要注意的地方是,請求攔截器中 config.url = '/users', 響應攔截器中 config.url = 'http://localhost:3000/users',因此加上一個標識isReuest來計算請求的全路徑服務器

/**
 * config: 請求數據
 * isReuest: 請求攔截器中 config.url = '/users', 響應攔截器中 config.url = 'http://localhost:3000/users',因此加上一個標識來計算請求的全路徑
 */
const getRequestIdentify = (config, isReuest = false) => {
  let url = config.url
  if (isReuest) {
    url = config.baseURL + config.url.substring(1, config.url.length)
  }
  return config.method === 'get' ? encodeURIComponent(url + JSON.stringify(config.params)) : encodeURIComponent(config.url + JSON.stringify(config.data))
}

請求攔截器

使用 cancel token 取消請求。網絡

這裏每一個請求經過傳遞一個 executor 函數到 CancelToken 的構造函數來建立 cancel token函數

// 添加請求攔截器
axios.interceptors.request.use(config => {
  // 發送請求以前,攔截重複請求(即當前正在進行的相同請求)
  let requestData = getRequestIdentify(config, true)
  removePending(requestData, true)

  config.cancelToken = new CancelToken((c) => {
    pending[requestData] = c
  })

  return config
}, error => {
  return Promise.reject(error)
})

取消請求

這一步是結合上面的請求攔截器執行的,取消重複請求的同時刪除記錄,而且在下面的響應攔截器也會調用該函數,即完成請求後刪除請求記錄。

// key: 請求標識;isRequest 完成請求後也須要執行刪除記錄,因此添加此參數避免執行無用操做
const removePending = (key, isRequest = false) => {
  if (pending[key] && isRequest) {
    pending[key]('取消重複請求')
  }
  delete pending[key] // 把這條記錄從 pending 中移除
}

請求異常處理

可使用響應攔截器來統一處理請求異常,例如,統一返回的數據的格式、統一處理異常報錯...

// 異常處理
axios.interceptors.response.use(response => {
  // 把已經完成的請求從 pending 中移除
  let requestData = getRequestIdentify(response.config)
  removePending(requestData)

  return {
    code: response.status,
    message: response.statusText,
    data: response.data
  }
}, err => {
  if (err && err.response) {
    switch (err.response.status) {
      case 400:
        err.message = '錯誤請求'
        break
      case 401:
        err.message = '未受權,請從新登陸'
        break
      case 403:
        err.message = '拒絕訪問'
        break
      case 404:
        err.message = '請求錯誤,未找到該資源'
        break
      case 405:
        err.message = '請求方法未容許'
        break
      case 408:
        err.message = '請求超時'
        break
      case 500:
        err.message = '服務器端出錯'
        break
      case 501:
        err.message = '網絡未實現'
        break
      case 502:
        err.message = '網絡錯誤'
        break
      case 503:
        err.message = '服務不可用'
        break
      case 504:
        err.message = '網絡超時'
        break
      case 505:
        err.message = 'http版本不支持該請求'
        break
      default:
        err.message = `鏈接錯誤${err.response.status}`
    }
    let errData = {
      code: err.response.status,
      message: err.message
    }
    // 統一錯誤處理能夠放這,例如頁面提示錯誤...
    console.log('統一錯誤處理: ', errData)
  }

  return Promise.reject(err)
})

疑問:

CancelToken 這一部分,還不是很清楚原理,但願你們指導一下~~~


參考地址:

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

相關文章
相關標籤/搜索