Vue2 项目简易封装 Axios

前言

日常开发前后端分离项目时,经常会使用到 axios 这个 HTTP 库。下面对它进行简易封装做了归纳整理。

目录

目录结构,我一般是这么安排的

|- src
|-- api
|--- index.js
|--- sendMsg.js
|-- utils
|--- index.js
|--- axios.js

通用

// utils/axios.js

import axios from 'axios'
import QS from 'qs' // 引入 qs 模块,用来序列化 post 类型的数据

// 环境切换
const BASE_URL = process.env.NODE_ENV === 'development'
    ? 'https://www.kaifa_dev.com'
    : 'https://www.shengchan_prod.com'

// 设置默认的请求超时时间
axios.defaults.timeout = 15000

// 创建实例
const instance = axios.create({
    baseURL: BASE_URL,
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    },
})

// 请求拦截器
instance.interceptors.request.use(function(config) {
    // 每次发送请求时,需要判断是否存在 token(vuex、cookie、localStorage)
    // 如果存在,那么将它添加在 header 上携带给后端,后端可以根据 token 判断用户登录状态
    // 如果存储的 token 过期了,须要在响应拦截里面做处理
    const myToken = localStorage.getItem('myToken')
    if (token) {
        config.headers.Authorization = token
    } else {
        // 引导用户登录
    }
    return config
})

// 响应拦截器
instance.interceptors.response.use(function(response) {
    // 处理返回结果
    if (response.status === 200) {
        // 成功
        return Promise.resolve(response)
    } else {
        // 失败
        return Promise.reject(error)
    }
}, function(error) {
    // 处理异常
    return Promise.reject(error)
})

// 封装 post 请求
export function post(url, data) {
    return new Promise((resolve, reject) => {
        instance.post(url, QS.stringify(data))
            .then((res) => {
                return resolve(res)
            })
            .catch((err) => {
                return reject(err)
            })
    })
}

// 封装 get 请求
export function get(url, params) {
    return new Promise((resolve, reject) => {
            instance.get(url, {
                params
            }).then(res => {
                return resolve(res)
            }).catch(err =>{
                return reject(err)
        })
    })
}
// utils/index.js

export * from './axios.js'
// api/sendMsg.js

import { get, post } from '../utils/index.js'

export const apiSendMsg = data => post('api/v1/sendMsg', data)
// api/index.js

export * from './sendMsg.js'

特殊

例如,token 过期了自动获取最新的,让用户无感知。

// utils/axios.js

import axios from 'axios'
import QS from 'qs'
import md5 from './md5' // md5 加密,参考:https://github.com/blueimp/JavaScript-MD5
import secret from './crypto'

const BASE_URL = process.env.NODE_ENV === 'development'
    ? 'https://www.kaifa_dev.com'
    : 'https://www.shengchan_prod.com'

const TOKEN_URL = process.env.NODE_ENV === 'development'
    ? 'https://www.kaifa_token_dev.com'
    : 'https://www.shengchan_token_prod.com'

// 获取 token
function fetchToken() {
    const instance = axios.create({
        baseURL: TOKEN_URL,
        transformRequest: [
            (data) => {
                let v = data
                v = QS.stringify(v)
                return v
            },
        ],
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        timeout: 15000,
    })

    return instance
        .post('/auth/getTokenExample', {
            appName: 'example',
            appKey: 'example',
        })
        .then((res) => {
            if (res.data.result === 'success') {
                localStorage.setItem('token', res.data.data)
                return Promise.resolve(res.data.data)
            }
            return Promise.reject(res)
        })
        .catch((err) => {
            return Promise.reject(err)
        })
}

// 创建通用 axios 实例
function createBaseInstance() {
    // 创建实例
    const instance = axios.create({
        baseURL: BASE_URL,
        transformRequest: [
            (data) => {
                let v = data
                v = QS.stringify(v)
                return v
            },
        ],
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        timeout: 15000,
    })

    // 请求拦截器
    const handleRequest = function(config) {
        const cfg = config
        const token = localStorage.getItem('token') || ''

        cfg.data = {
            token,
            jsonData: secret.encrypt(config.data.jsonData),
            sign: md5(`jsonData=${config.data.jsonData}&example=123456`), // example 与后端约定
        }
        return cfg
    }

    // 响应拦截器
    const handleResponse = async function(response) {
        const responseData = response
        if (responseData.data.result === 'success') {
            return Promise.resolve(responseData.data)
        }

        if (responseData.data.errorCode === 1000) {
            // 重新获取 token,然后再请求上一个接口
            const tokenRes = await fetchToken()
            const configData = QS.parse(responseData.config.data)
            configData.token = tokenRes
            configData.jsonData = secret.decrypt(configData.jsonData)
            responseData.config.data = configData
            return instance.request(responseData.config)
        }

        return Promise.reject(responseData)
    }

    // 处理异常
    const handleError = function(error) {
        if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
            return Promise.reject(new Error('加载超时'))
        }
        return Promise.reject(error)
    }

    instance.interceptors.request.use(handleRequest, handleError)
    instance.interceptors.response.use(handleResponse, handleError)

    return instance
}

const request = createBaseInstance()

export default request
// utils/index.js

import request from './axios.js'

export { request }
// api/sendMsg.js

import { request } from '@/utils'

export const apiSendMsg = data => request.post('api/v1/sendMsg', data)
// api/index.js

export * from './sendMsg.js'
// crypto.js 参考:https://github.com/brix/crypto-js

import CryptoJS from 'crypto-js'

const key = CryptoJS.enc.Utf8.parse('example') // 与后端约定
const iv = CryptoJS.enc.Utf8.parse('example') // 与后端约定

export default {
    // 加密 TripleDES
    encrypt(word) {
        let ciphertext = ''
        ciphertext = CryptoJS.TripleDES.encrypt(word, key, {
            iv,
            mode: CryptoJS.mode.ECB, // ECB 模式
            padding: CryptoJS.pad.Pkcs7, // padding 处理
        })
        return ciphertext.toString()
    },

    // 解密
    decrypt(word) {
        let ciphertext = ''
            ciphertext = CryptoJS.TripleDES.decrypt(word, key, {
            iv,
            mode: CryptoJS.mode.ECB, // ECB 模式
            padding: CryptoJS.pad.Pkcs7, // padding 处理
        })

        // 解析数据后转为 UTF-8
        return ciphertext.toString(CryptoJS.enc.Utf8)
    },
}

结语

以上对 axios 封装仅提供思路,请求前或响应后具体如何处理,还需要根据自己业务需求和后端返回的状态数据来定。

本文到这里就结束了,希望这篇文章对你有所帮助。