第十四章:配置基于 Axios 的 HTTP 数据请求

一、安装 Axios 和 qs

pnpm add axios
pnpm add qs
pnpm add @types/qs --save-dev

二、创建 src/utils/request.ts 文件

// src/utils/request.ts

import type { AxiosRequestConfig, AxiosResponse } from 'axios'
import { message, Modal } from 'ant-design-vue'
import axios, { CanceledError } from 'axios'
import qs from 'qs'
import { ResCodeEnum } from '@/api/enum/common.enum'
import { useUserStore } from '@/store/modules/user'

export interface RequestOptions extends AxiosRequestConfig {
    /** 是否直接将数据从响应中提取出,例如直接返回 res.data,而忽略 res.code 等信息 */
    isReturnResult?: boolean

    /** 请求成功时提示信息 */
    successMsg?: string

    /** 请求失败时提示信息 */
    errorMsg?: string

    /** 成功时,是否显示后端返回的成功信息 */
    showSuccessMsg?: boolean

    /** 失败时,是否显示后端返回的失败信息 */
    showErrorMsg?: boolean

    /**
   * 请求类型; 默认会配置成 json
   * 当是 form 时,请求头中的 'Content-Type' 会配置成 'multipart/form-data'
   * 当是 json 时,请求头中的 'Content-Type' 会配置成 'application/json'
   */
    requestType?: 'json' | 'form'
}

const UNKNOWN_ERROR = '未知错误,请重试'

// 创建一个 axios 实例
const service = axios.create({
    // 真实请求的路径前缀
    baseURL: import.meta.env.VITE_BASE_API_URL,
    // 指定请求超时的毫秒数(0 表示无超时时间),如果请求超过 10000 毫秒,请求将被中断
    timeout: 10000,

    /**
     * 负责将 params 序列化的函数
     */
    paramsSerializer(params) {
        // qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }); // 输出: "a[]=b&a[]=c"
        // qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }); // 输出: "a[0]=b&a[1]=c"
        // qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }); // 输出: "a=b&a=c"
        // qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' }); // 输出: "a=b,c"
        return qs.stringify(params, { arrayFormat: 'brackets' })
    },
})

// 添加请求拦截器
service.interceptors.request.use(
    (config) => {
        const userStore = useUserStore()
        const token = userStore.token
        if (token && config.headers) {
            // 向请求头中添加 token 信息
            Object.defineProperty(config.headers, 'Authorization', `Bearer ${token}`)
        }
        return config
    },
    (error) => {
        Promise.reject(error)
    },
)


// 添加响应拦截器
service.interceptors.response.use(
    (response: AxiosResponse<{ code: number, [key: string]: any }>) => {
        const res = response.data

        if (res.code !== ResCodeEnum.SUCCESS) {
            message.error(res.message || UNKNOWN_ERROR)
            //  未登录 or token过期
            if ([ResCodeEnum.NOT_LOGIN, ResCodeEnum.TOKEN_EXPIRATION].includes(res.code)) {
                Modal.confirm({
                    title: '警告',
                    content: res.message || '账号异常,您可以取消停留在该页上,或重新登录',
                    okText: '重新登录',
                    cancelText: '取消',
                    onOk: () => {
                        localStorage.clear()
                       window.location.reload()
                    },
                })
            }

            // throw other
            const error = new Error(res.message || UNKNOWN_ERROR) as Error & { code: any }
            error.code = res.code
            return Promise.reject(error)
        }
        else {
            return response
        }
    },
    (error) => {
        if (!(error instanceof CanceledError)) {
            // 处理 422 或者 500 的错误异常提示
            const errMsg = error?.response?.data?.message ?? UNKNOWN_ERROR
            message.error({ content: errMsg, key: errMsg })
            error.message = errMsg
        }
        return Promise.reject(error)
    },
)

export function request<T = any>(url: string, config: RequestOptions): Promise<T>
export function request<T = any>(config: RequestOptions): Promise<T>
export async function request<T = any>(_url: string | RequestOptions, _config: RequestOptions = {}): Promise<T> {
    const url = typeof _url === 'string' ? _url : _url.url
    const config = typeof _url === 'string' ? _config : _url

    try {
        // 兼容 from data 文件上传的情况
        const { requestType = 'json', ...rest } = config

        const response = (await service.request({
            url,
            ...rest,
            headers: {
                ...rest.headers,
                ...{ 'Content-Type': requestType === 'form' ? 'multipart/form-data' : 'application/json' },
            },
        })) as AxiosResponse<T>

        const { data } = response
        const code = (data as any)?.code
        const message = (data as any)?.message

        const hasSuccess = data && Reflect.has(data, 'code') && code === ResCodeEnum.SUCCESS

        if (hasSuccess) {
            const { successMsg, showSuccessMsg } = config
            if (successMsg) {
                message.success(successMsg)
            }
            else if (showSuccessMsg && message) {
                message.success(message)
            }
         }

        return data
    }
    catch (error: any) {
        return Promise.reject(error)
    }
}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容