手写VUE后台管理系统10 - 封装Axios实现异常统一处理

目录

    • 前后端交互约定
    • 安装
    • 创建Axios实例
    • 拦截器
    • 封装请求方法
    • 业务异常处理


在这里插入图片描述

axios 是一个易用、简洁且高效的http库
axios 中文文档:http://www.axios-js.com/zh-cn/docs/


前后端交互约定

在本项目中,前后端交互统一使用 application/json;charset=UTF-8 的请求方式,后端返回对象统一为如下格式

export interface ResponseBody<T = any> {
    status: boolean,	// 业务处理状态,true表示正常,false表示异常
    code: string		// 业务处理状态码
    message: string,	// 提示信息
    data?: T			// 业务处理返回数据
}

安装

yarn add axios

创建Axios实例

src 目录下创建 http 目录,http 请求相关的文件都放置于该目录

创建 axios.ts 文件,用于定义 axios

// axios.ts
const instance: AxiosInstance = axios.create({
    baseURL: import.meta.env.VITE_APP_BASE_API,
    timeout: 60000,
    headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})

其中,import.meta.env.VITE_APP_BASE_API.env 中配置的环境变量,不同环境使用不同的请求地址

拦截器

通过 instance.interceptors.request.use 实现前置拦截器,发起请求前执行,用于对请求对象进行加工处理。

下面这段代码主要做的事情就是将 token 设置到请求头中。

// axios.ts
async function requestHandler(config: InternalAxiosRequestConfig & RequestConfigExtra): Promise<InternalAxiosRequestConfig> {
    if (config.modulePrefix) {
        config.url = config.modulePrefix + config.url
    }
    const token = useAuthorization()
    if (token.value && config.token !== false) {
        config.headers.set("Authorization", token.value)
    }
    console.log("execute http request:" + config.url)
    return config
}

instance.interceptors.request.use(requestHandler)

RequestConfigExtra 为自定义参数,在本项目中定义如下,可根据需要自行扩展。

export interface RequestConfigExtra {
    // 模块前缀
    modulePrefix?: string,
    // 发起请求时,是否需要在请求头中附加 token
    token?: boolean,
    // 成功处理函数,默认为 false,如果为 false,则什么都不做,如果为 true,则自动提示成功信息,如果为 Function,则自定义处理结果
    success?: boolean | ((response: ResponseBody<any>) => void),
    // 失败处理函数,默认为 true,如果为 false,则什么都不做,如果为 true,则自动提示失败信息,如果为 Function,则自定义处理结果
    error?: boolean | ((response: ResponseBody<any>) => void),
}

通过 instance.interceptors.response.use 实现后置拦截器,对后端返回数据进行处理。

function responseHandler(response: any): ResponseBody<any> | AxiosResponse<any> | Promise<any> | any {
    return response.data
}

function errorHandler(errorInfo: AxiosError): Promise<any> {
    if (errorInfo.response) {
        const { data, status, statusText } = errorInfo.response as AxiosResponse<ResponseBody>
        if (status === 401) {
            const token = useAuthorization()
            token.value = null
            message.error(data?.message || statusText)
            router.push({path: '/login', query: { 
                redirect: router.currentRoute.value.fullPath
            }})
        } else {
            message.error(data?.message || statusText)
        } 
    }
    return Promise.reject(errorInfo)
}

instance.interceptors.response.use(responseHandler, errorHandler)

errorHandler 方法对系统异常进行了统一处理,如果后端返回的 status 不是 200,则会执行该处理方法,如果返回 401,表示没有通过登录鉴权,自动跳转登录页,如果是其它异常码,则提示错误信息。

封装请求方法

这里对 restful 常用的四种请求方式进行进一步的封装

// axios.ts
function instancePromise<R = any, T = any>(options: AxiosRequestConfig<T> & RequestConfigExtra): Promise<ResponseBody<R>> {
    return new Promise((resolve, reject) => {
        instance.request<any, ResponseBody<R>>(options)
            .then((res) => {
                try {
                    resolve(responseBodyHandle(res, options))
                } catch (err) {
                    reject(err || new Error('response handle error!'))
                }
            })
            .catch((e: Error | AxiosError) => {
                reject(e)
            })
    })
}

export function doGet<R = any, T = any>(url: string, params?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
    const options = {
        url,
        params,
        method: RequestEnum.GET,
        ...config,
    }
    return instancePromise<R, T>(options)
}

export function doPost<R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
    const options = {
        url,
        data,
        method: RequestEnum.POST,
        ...config,
    }
    return instancePromise<R, T>(options)
}

export function doPut<R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
    const options = {
        url,
        data,
        method: RequestEnum.PUT,
        ...config,
    }
    return instancePromise<R, T>(options)
}

export function doDelete<R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
    const options = {
        url,
        data,
        method: RequestEnum.DELETE,
        ...config,
    }
    return instancePromise<R, T>(options)
}

业务异常处理

在拦截器中,我们对系统异常进行了统一处理,在实际项目中,更多的情况是前端请求没有通过后端的业务校验,后端返回错误信息,前端进行提示。

创建一个业务异常类

export class ResponseBodyError extends Error {
    code: string
    message: string
    cause: any

    constructor({ code, message, cause }: { code: string, message: string, cause?: any }) {
        super();
        this.code = code;
        this.message = message;
        this.cause = cause
    }
}

实现业务异常统一处理,通过在请求时定义 RequestConfigExtra 中的参数来实现对后端返回结果的提示。

比如:查询请求,设置为 {success:false, error: true},如果请求失败,提示错误信息,如果请求成功,不作处理。业务请求,设置为 {success: true, error: true},如果请求失败,提示错误信息,如果处理成功,提示操作成功。

function responseBodyHandle<R = any, T = any>(response: ResponseBody<R>, options: AxiosRequestConfig<T> & RequestConfigExtra): any {
    const { status, message:msg, code, data } = response
    const { success, error } = options
    if (status === true) {
        if (success === true) {
            message.success(msg ?? "操作成功")
        } else if (isFunction(success)) {
            success(response)
        }
        return response
    } else {
        if (isFunction(error)) {
            error(response)
        } else if (error !== false) {
            message.error(msg ?? "操作失败")
        }
        throw new ResponseBodyError({ code, msg })
    }
}

完整代码如下:

// axios.ts
/**
 * 创建 Axios 实例
 */
const instance: AxiosInstance = axios.create({
    baseURL: import.meta.env.VITE_APP_BASE_API,
    timeout: 60000,
    headers: { 'Content-Type': 'application/json;charset=UTF-8' },
})

/**
 * 前置拦截器
 */
async function requestHandler(config: InternalAxiosRequestConfig & RequestConfigExtra): Promise<InternalAxiosRequestConfig> {
    if (config.modulePrefix) {
        config.url = config.modulePrefix + config.url
    }
    const token = useAuthorization()
    if (token.value && config.token !== false) {
        config.headers.set(authorizationHeader, authorizationValue())
    }
    console.log("execute http request:" + config.url)
    return config
}

/**
 * 后置拦截器
 */
function responseHandler(response: any): ResponseBody<any> | AxiosResponse<any> | Promise<any> | any {
    return response.data
}

/**
 * 系统异常统一处理函数
 */
function errorHandler(errorInfo: AxiosError): Promise<any> {
    if (errorInfo.response) {
        const { data, status, statusText } = errorInfo.response as AxiosResponse<ResponseBody>
        if (status === 401) {
            const token = useAuthorization()
            token.value = null
            message.error(data?.message || statusText)
            router.push({path: '/login', query: { 
                redirect: router.currentRoute.value.fullPath
            }})
        } else {
            message.error(data?.message || statusText)
        } 
    }
    return Promise.reject(errorInfo)
}

instance.interceptors.request.use(requestHandler)
instance.interceptors.response.use(responseHandler, errorHandler)

/**
 * 业务异常统一处理函数
 */
function responseBodyHandle<R = any, T = any>(response: ResponseBody<R>, options: AxiosRequestConfig<T> & RequestConfigExtra): any {
    const { status, message:msg, code, data } = response
    const { success, error } = options
    if (status === true) {
        if (success === true) {
            message.success(msg ?? "操作成功")
        } else if (isFunction(success)) {
            success(response)
        }
        return response
    } else {
        if (isFunction(error)) {
            error(response)
        } else if (error !== false) {
            message.error(msg ?? "操作失败")
        }
        throw new ResponseBodyError({ code, msg })
    }
}

function instancePromise<R = any, T = any>(options: AxiosRequestConfig<T> & RequestConfigExtra): Promise<ResponseBody<R>> {
    return new Promise((resolve, reject) => {
        instance.request<any, ResponseBody<R>>(options)
            .then((res) => {
                try {
                    resolve(responseBodyHandle(res, options))
                } catch (err) {
                    reject(err || new Error('response handle error!'))
                }
            })
            .catch((e: Error | AxiosError) => {
                reject(e)
            })
    })
}

export function doGet<R = any, T = any>(url: string, params?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
    const options = {
        url,
        params,
        method: RequestEnum.GET,
        ...config,
    }
    return instancePromise<R, T>(options)
}

export function doPost<R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
    const options = {
        url,
        data,
        method: RequestEnum.POST,
        ...config,
    }
    return instancePromise<R, T>(options)
}

export function doPut<R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
    const options = {
        url,
        data,
        method: RequestEnum.PUT,
        ...config,
    }
    return instancePromise<R, T>(options)
}

export function doDelete<R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> {
    const options = {
        url,
        data,
        method: RequestEnum.DELETE,
        ...config,
    }
    return instancePromise<R, T>(options)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/237389.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

appium安卓app自动化,遇到搜索框无搜索按钮元素时无法搜索的解决方案

如XX头条&#xff0c;搜索框后面有“搜索”按钮&#xff0c;这样实现搜索操作较为方便。 但有些app没有设置该搜索按钮&#xff0c;初学者就要花点时间去学习怎么实现该功能了&#xff0c;如下图。 这时候如果定位搜索框&#xff0c;再点击操作&#xff0c;再输入文本后&#x…

【QT入门】基础知识

一.认识Qt qt是一套应用程序开发库&#xff0c;与MFC不同是跨平台的开发类库&#xff0c;主要用来开发图形界面。完全面向对象容易扩展。 优点&#xff1a;1.封装性强&#xff0c;简单易学 2.跨平台 3.独立编译为本地代码 二.qt工程 1.常见的工程文件有这两种…

2024 年 SEO 现状

搜索引擎优化&#xff08;SEO&#xff09;一直以来都是网络知名度和成功的基石。随着我们踏上 2024 年的征程&#xff0c;SEO领域正在经历重大变革&#xff0c;有些变革已经开始&#xff0c;这对企业、创作者和营销人员来说既是挑战也是机遇。 语音搜索 语音搜索曾是一个未来…

HeartBeat监控Mysql状态

目录 一、概述 二、 安装部署 三、配置 四、启动服务 五、查看数据 一、概述 使用heartbeat可以实现在kibana界面对 Mysql 服务存活状态进行观察&#xff0c;如有必要&#xff0c;也可在服务宕机后立即向相关人员发送邮件通知 二、 安装部署 参照章节&#xff1a;监控组件…

【小白专用】MySQL查询数据库所有表名及表结构其注释

一、先了解下INFORMATION_SCHEMA 1、在MySQL中&#xff0c;把INFORMATION_SCHEMA看作是一个数据库&#xff0c;确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名&#xff0c;数据库的表&#xff0c;表栏的数据类型与访问权 限等。在INF…

HarmonyOS编译开源native库(OpenSSL实例)

前言 近期项目要开始做鸿蒙版本&#xff0c;有一部分依赖native的代码也需要迁移&#xff0c;某个native模块依赖openssl&#xff0c;需要在鸿蒙下重新编译openssl才行。一开始找了很多相关文档都没有得到方法&#xff0c;无奈只能自己凭经验慢慢试&#xff0c;最后还是成功了…

.net 安装Postgresql驱动程序ngpsql

.net 安装Postgresql驱动程序ngpsql 最近搞一个物联网项目&#xff0c;需要采集fanuc数控机床的数据&#xff0c;厂家提供的API只支持windows&#xff0c;所以就决定C#开发&#xff0c;数据库用postgresql&#xff0c; 安装数据库驱动一波三折。 作为一个讨厌微软的老程序猿&…

Postman高级应用——变量、流程控制、调试、公共函数、外部数据文件

Postman 提供了四种类型的变量 环境变量&#xff08;Environment Variable&#xff09; 不同的环境&#xff0c;使用不同的环境变量&#xff0c;例如&#xff1a;测试过程中经常会用到 测试环境&#xff0c;外网环境等 全局变量&#xff08;Global Variable&#xff09; 所有的…

使用PyTorch II的新特性加快LLM推理速度

Pytorch团队提出了一种纯粹通过PyTorch新特性在的自下而上的优化LLM方法&#xff0c;包括: Torch.compile: PyTorch模型的编译器 GPU量化:通过降低精度操作来加速模型 推测解码:使用一个小的“草稿”模型来加速llm来预测一个大的“目标”模型的输出 张量并行:通过在多个设备…

认识lambda架构(架构师考试复习)

Lambda架构主要分为三层&#xff0c;批处理层、加速层和服务层。 如下图所示&#xff1a; &#xff08;1&#xff09;批处理层&#xff08;Batch Layer&#xff09;&#xff1a;存储数据集&#xff0c;在数据集上预先计算查询函数&#xff0c;并构建查询对应的view。Batch Lay…

Unity-小工具-LookAt

Unity-小工具-LookAt &#x1f959;介绍 &#x1f959;介绍 &#x1f4a1;通过扩展方法调用 gameObject.LookAtTarget&#xff0c;让物体转向目标位置 &#x1f4a1;gameObject.StopLookat 停止更新 &#x1f4a1;可以在调用时传入自动停止标记&#xff0c;等转向目标位置后自…

C语言学习----指针和数组

&#x1f308;这篇blog记录一下指针学习~ 主要是关于指针和数组之间的关系&#xff0c;还有指针的使用等~ &#x1f34e;指针变量是一个变量 其本身也有一个地址 也需要存放&#xff0c;就和int char等类型一样的&#xff0c;也需要有一个地址来存放它 &#x1f34c;而指针变量…

打包less

接HTML和css之后对less进行打包 1.在之前的文件夹里的src文件夹创建一个less文件 2.打开webpack——>中文文档——>Loader——>less—loader 3.复制下图代码到终端 4.复制下图内容到webpack.config.js脚本 5.在src里的js文件年引入less文件 6.在终端运行 npm run te…

实现安装“自由化”!在Windows 11中如何绕过“您尝试安装的应用程序未通过微软验证”

这篇文章描述了如果你不能安装应用程序,而是当你在Windows 11中看到消息“您尝试安装的应用程序未通过微软验证”时该怎么办。完成这些步骤将取消你安装的应用程序必须经过Microsoft验证的要求。 使用设置应用程序 “设置”应用程序提供了绕过此警告消息的最简单方法,以便你…

C# OpenCvSharp DNN 部署YOLOV6目标检测

目录 效果 模型信息 项目 代码 下载 C# OpenCvSharp DNN 部署YOLOV6目标检测 效果 模型信息 Inputs ------------------------- name&#xff1a;image_arrays tensor&#xff1a;Float[1, 3, 640, 640] -------------------------------------------------------------…

[c++]—vector类___基础版(带你了解vector熟练掌握运用)

&#x1f469;&#x1f3fb;‍&#x1f4bb;作者:chlorine 目录 &#x1f393;标准库类型vector &#x1f393;定义和初始化vector的对象 &#x1f4bb;列表初始化vector对象 &#x1f4bb;创建指定数量的元素 &#x1f576;️值初始化 ❗列表初始化还是值初始化&#xf…

Vuex快速上手

一、Vuex 概述 目标&#xff1a;明确Vuex是什么&#xff0c;应用场景以及优势 1.是什么 Vuex 是一个 Vue 的 状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;Vuex 是一个插件&#xff0c;可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。例如&#xff1a;购…

DevEco Studio 3.1IDE环境配置(HarmonyOS 3.1)

DevEco Studio 3.1IDE环境配置&#xff08;HarmonyOS 3.1&#xff09; 一、安装环境 操作系统: Windows 10 专业版 IDE:DevEco Studio 3.1 SDK:HarmonyOS 3.1 二、环境安装 IDE下载地址&#xff1a;HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 IDE的安装就是…

关于uniapp X 的最新消息

uni-app x 是什么&#xff1f; uni-app x&#xff0c;是下一代 uni-app&#xff0c;是一个跨平台应用开发引擎。 uni-app x 没有使用js和webview&#xff0c;它基于 uts 语言。在App端&#xff0c;uts在iOS编译为swift、在Android编译为kotlin&#xff0c;完全达到了原生应用的…

计算机网络(三) | 数据链路层 PPP协议、广播CSMA/CD协议、集线器、交换器、扩展and高速以太网

文章目录 1 数据链路基本概念和问题1.1 基本概念1.2 基本问题&#xff08;1&#xff09;封装成帧&#xff08;2&#xff09;透明传输&#xff08;3&#xff09;差错控制 2.数据链路层协议2.1 点对点 PPP协议2.1.1 需要实现的2.1.2 PPP组成2.1.3 帧格式2.1.4 工作流程 2.2 广播 …