Skip to content
字数
1431 字
阅读时间
7 分钟

概述

背景:面对大型项目,可以对 api 进行统一管理,有利于后期维护

二次封装 axios 的几个核心点:

  • 请求前 token 验证
  • 响应前处理不同状态码,响应出错是统一的 msg 错误描述
  • 封装 get,post,patch 等 http 请求
  • 对接配置文件的 baseApimockApi

公共依赖配置引入

js
/**
 * 二次封装网络请求模块
 */

import axios from 'axios'
import config from '@/config/index'
import { ElMessage } from 'element-plus'

const TOKEN_INVALID = 'Token认证失败,请重新登录'
const NETWORK_ERROR = '网络请求失败,请稍后再试'

const ajax = axios.create({
    baseURL: config.baseApi,
    timeout: 8 * 1000
})

请求响应拦截

js
// 配置请求拦截
ajax.interceptors.request.use(
    (req) => {
        // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
        // const token = this.$store.state.Authorization
        // token&&(req.headers.Authorization = token)
        const { headers } = req
        if (!headers.authorization) {
            headers['authorization'] = 'Bearer JIE'
        }
        return req
    },
    (error) => {
        return Promise.error(error)
    }
)

// 配置响应拦截
ajax.interceptors.respond.use(
    (res) => {
        const { code, data, msg } = res
        if (code === 200) {
            return data
        } else if (code === 50001) {
            ElMessage.error(TOKEN_INVALID)
            return Promise.reject(TOKEN_INVALID)
        } else {
            ElMessage.error(msg || NETWORK_ERROR)
            return Promise.reject(msg || NETWORK_ERROR)
        }
    },
    // 服务器返回不是200的情况
    (error) => {
        if(error.response.status){
            switch (error.response.status){
                //401 未登录,未登录则跳转登录页面,并携带当前页面的路径
                //在登录成功后返回当前页面,这一步需要在登录页操作
                case 401:
                    this.$router.replace({
                        path: '/login',
                        query: {
                            redirect: this.$router.currentRoute.fullpath
                        }
                    })
                break
                //403 token过期,登录过期对用户进行提示
                //清除本地token和清空vuex中的token对象,跳转到登录页面
                case 403:
                    this.$toast({
                        message: '登录过期,请重新登录',
                        duration: 1000,
                        forbidClick: true
                    })
                    //清除token
                    localStorage.removeItem('Authorization')
                    this.$store.commit('changeLogin',null)
                    //跳转登录页面,并将要浏览的页面的fullPath传过去,登录成功后跳转需要访问的页面
                    setTimeout(() => {
                        this.$router.replace({
                            path: '/Login',
                            query: {
                                redirect: this.$router.currentRoute.fullPath
                            }
                        })
                    },1000)
                break
                //404请求不存在
                case 404:
                    this.$toast({
                        message: '网络请求不存在',
                        duration: 1500,
                        forbidClick: true
                    })
                break
                //其他错误,直接抛出错误提示
                default:
                    this.$toast({
                        message: error.response.data.message,
                        duration: 1500,
                        forbidClick: true
                    })
            }
            return Promise.reject(error.response)
        }
    }
)

对接 config 配置文件

js
/**
 * axios请求核心函数
 */

const request = (options) => {
    // 如果不传入请求方法,默认为 get 请求
    options.method = options.method || 'get'
    // 若为 get 请求,则将 data 转换为 params
    if (options.method.toLowerCase() === 'get') options.params = options.data
    if (config.dev === 'prod') {
        // 如果是生产环境,则使用baseApi
        ajax.defaults.baseURL = config.baseApi
    } else if (options['mock']) {
        // 如果这个请求传入了 mock=true 选项,则使用 MOCK api
        ajax.defaults.baseURL = config.mockApi
    } else {
        // config 文件传入 MOCK 参数? 传入了那么我就用mockApi
        ajax.defaults.baseURL = config.mock ? config.mockApi : config.baseApi
    }
}

封装 http 请求

js
// 封装 request.get...等方法
['get', 'post', 'put', 'delete', 'patch'].forEach(item => {
    request[item] = (url, data, options) => {
        return request({
            url,
            data,
            ...options,
            method: item
        })
    }
})

统一写法

js
/**
 * 二次封装网络请求模块
 */

import axios from 'axios'
import config from '@/config/index'
import { ElMessage } from 'element-plus'

const TOKEN_INVALID = 'Token认证失败,请重新登录'
const NETWORK_ERROR = '网络请求失败,请稍后再试'

const ajax = axios.create({
    baseURL: config.baseApi,
    timeout: 8 * 1000
})

// 配置请求拦截
ajax.interceptors.request.use(
    (req) => {
        // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
        // const token = this.$store.state.Authorization
        // token&&(req.headers.Authorization = token)
        const { headers } = req
        if (!headers.authorization) {
            headers['authorization'] = 'Bearer JIE'
        }
        return req
    },
    (error) => {
        return Promise.error(error)
    }
)

// 配置响应拦截
ajax.interceptors.respond.use(
    (res) => {
        const { code, data, msg } = res
        if (code === 200) {
            return data
        } else if (code === 50001) {
            ElMessage.error(TOKEN_INVALID)
            return Promise.reject(TOKEN_INVALID)
        } else {
            ElMessage.error(msg || NETWORK_ERROR)
            return Promise.reject(msg || NETWORK_ERROR)
        }
    },
    // 服务器返回不是200的情况
    (error) => {
        if(error.response.status){
            switch (error.response.status){
                //401 未登录,未登录则跳转登录页面,并携带当前页面的路径
                //在登录成功后返回当前页面,这一步需要在登录页操作
                case 401:
                    this.$router.replace({
                        path: '/login',
                        query: {
                            redirect: this.$router.currentRoute.fullpath
                        }
                    })
                break
                //403 token过期,登录过期对用户进行提示
                //清除本地token和清空vuex中的token对象,跳转到登录页面
                case 403:
                    this.$toast({
                        message: '登录过期,请重新登录',
                        duration: 1000,
                        forbidClick: true
                    })
                    //清除token
                    localStorage.removeItem('Authorization')
                    this.$store.commit('changeLogin',null)
                    //跳转登录页面,并将要浏览的页面的fullPath传过去,登录成功后跳转需要访问的页面
                    setTimeout(() => {
                        this.$router.replace({
                            path: '/Login',
                            query: {
                                redirect: this.$router.currentRoute.fullPath
                            }
                        })
                    },1000)
                break
                //404请求不存在
                case 404:
                    this.$toast({
                        message: '网络请求不存在',
                        duration: 1500,
                        forbidClick: true
                    })
                break
                //其他错误,直接抛出错误提示
                default:
                    this.$toast({
                        message: error.response.data.message,
                        duration: 1500,
                        forbidClick: true
                    })
            }
            return Promise.reject(error.response)
        }
    }
)

/**
 * axios请求核心函数
 */

const request = (options) => {
    // 如果不传入请求方法,默认为 get 请求
    options.method = options.method || 'get'
    // 若为 get 请求,则将 data 转换为 params
    if (options.method.toLowerCase() === 'get') options.params = options.data
    if (config.dev === 'prod') {
        // 如果是生产环境,则使用baseApi
        ajax.defaults.baseURL = config.baseApi
    } else if (options['mock']) {
        // 如果这个请求传入了 mock=true 选项,则使用 MOCK api
        ajax.defaults.baseURL = config.mockApi
    } else {
        // config 文件传入 MOCK 参数? 传入了那么我就用mockApi
        ajax.defaults.baseURL = config.mock ? config.mockApi : config.baseApi
    }
}

// 封装request.get...等方法
['get', 'post', 'put', 'delete', 'patch'].forEach(item => {
    request[item] = (url, data, options) => {
        return request({
            url,
            data,
            ...options,
            method: item
        })
    }
})

export default request

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

撰写