Axios二次封裝的成熟方案

架構(gòu)二次封裝需要實現(xiàn)如下功能

  • API下發(fā)
  • 取消多余請求 - 統(tǒng)一處理
  • HTTP錯誤統(tǒng)一處理
  • 服務器特殊錯誤處理
  • API-mock方案

封裝目標:
1)頁面只管API調(diào)用,與服務端的接口定義限制在封裝結(jié)構(gòu)中。
2)在封裝結(jié)構(gòu)中設計開關,切換api-mock時完全不需要頁面進行修改。
3)前后端分離的更干脆,前端頁面更干凈,接口簡潔易懂,只處理返回邏輯。
?


上代碼

# Axios二次封裝Service

文件 api/service.js


import axios from 'axios'
import { Message } from 'element-ui'

const getUrlKey = config => `SYSNAME_PC_${config.url}`
const cancelToken = {} // 被取消的請求池
const CancelToken = axios.CancelToken

/**
 * @func 取消多余請求-統(tǒng)一處理
 * @desc type 操作類別
 * @desc config 當前請求的配置
 */
const cancelThis = (type, config) => {
    const key = getUrlKey(config)
    if (type === 'check') {
        if (cancelToken[key]) {
            cancelToken[key]()
        } else {
            config.cancelToken = new CancelToken(c => {
                cancelToken[key] = c
            })
        }
    } else if (type === 'remove') {
        delete cancelToken[key]
    }
}

const cancelTokenWhiteList = []

/**
 * @func axios二次封裝為service
 */
const service = axios.create({
    baseURL: process.env.VUE_APP_BASE_URL,
    withCredentials: true, // 跨域攜帶 cookie
    timeout: 15000
})

// request interceptor
axios.interceptors.request.use(
    config => {
        if (cancelTokenWhiteList.indexOf(config.url) !== -1) {
            cancelThis('check', config)
        }
        config.crossDomain = true
        config.headers.common = {
            'Content-Type': 'application/json;charset=UTF-8',
            'X-ER-System-Info': 'default',
            'Accept-Language': 'zh-CN'
        }
        return config
    },
    err => {
        console.log('request interceptor err:', err)
        return Promise.reject(err)
    }
)

// response interceptor
axios.interceptors.response.use(
    async response => {
        cancelThis('remove', response.config)
        const result = response.data || {}
        const { code, data, message } = result
        if (code === '9999' || code === '-1') {
            Message.closeAll()
            Message.error(message || '服務器異常,請稍后重試或聯(lián)系我們')
            return Promise.reject(new Error(message || 'Error'))
        } else {
            return data
        }
    },
    // http request err
    err => {
        console.log('response interceptor error: ', err)
        const res = err.response
        // 請求執(zhí)行成功,屬Http Code錯誤
        if (res) {
            const { status, data } = JSON.parse(JSON.stringify(res)) || {}
            if (Number(status) === 500) {
                Message.error(data.message || '服務器異常,請您稍后重試或聯(lián)系我們')
            } else {
                handleHttpCodeError(Number(status))
            }
        } else {
            const ErrStr = err.toString() || ''
            if (ErrStr.toString().indexOf('Cancel') !== -1) { // 請求被主動取消,忽略
                return
            } else {
                // 請求執(zhí)行失敗,斷網(wǎng)/超時
                handleNotExceptError(ErrStr)
            }
        }
    }
)

/**
 * @func 通用的HttpCode異常處理
 * @param {Number} code http異常碼
 */
function handleHttpCodeError(code) {
    const HttpError = {
        400: '請求錯誤',
        403: '拒絕訪問',
        404: '請求地址錯誤',
        408: '請求超時',
        500: '服務器開個小差,請稍后再試',
        501: '服務器開個小差,請稍后再試',
        502: '服務器開個小差,請稍后再試',
        503: '服務器開個小差,請稍后再試',
        504: '服務器開個小差,請稍后再試',
        505: 'HTTP版本不受支持'
    }
    Message.error(HttpError[code])
}

/**
 * @func 請求執(zhí)行失敗,斷網(wǎng)/超時/未知錯誤
 * @param {Object} err 錯誤信息載體
 */
function handleNotExceptError(ErrStr) {
    Message.closeAll()
    if (ErrStr.indexOf('Request failed') !== -1) {
        Message.error('當前網(wǎng)絡不可用,請檢查網(wǎng)絡設置')
    } else if (ErrStr.indexOf('timeout') !== -1) {
        Message.error('您當前網(wǎng)絡較差,請切換網(wǎng)絡活稍后重試')
    } else {
        Message.error('發(fā)生未知錯誤,請稍后重試')
    }
}

export default service

?


# API封裝

同級文件, api/login.js

// api/login.js
import service from './service.js'
//實現(xiàn)api-mock
import {
  initLoginMock,
  getUserInfoMock
} from './api-mock/login.js'

const mock = process.env.VUE_APP_MOCK === 'true'
// post
export function initLogin(data) {
  // mock-api策略仁者見仁、智者見智,也可以通過url匹配取值,本方案個人比較喜歡。
  if (mock) return initLoginMock()
  return service({
    url: 'v1/user/login',
    method: 'post',
    data
  })
}

// get
export function getUserInfo(params) {
  if (mock) return getUserInfoMock()
  return service({
    url: 'v1/user/getUserInfo',
    method: 'get',
    params
  })
}

?


# API調(diào)用

頁面 login.vue

<script>
import { initLogin, getUserInfo } from '@/api/login.js'

// 登錄頁初始化
initLogin({macAdress: 'adress'}).then(data => {
  console.log(data)
}).catch(e => {
  console.log(e)
})
</script>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容