以配置的方式开关axios拦截器功能

前景提要:
ts 简易封装 axios,统一 API

背景

axios 很多额外功能都是基于拦截器实现。有些功能想要全局使用,因此将拦截器注册在全局。比如重复请求过滤。但也有一小部分请求不希望进行过滤,比如并发上传文件。

因此希望可以在具体的请求方法上,通过配置 config 从而决定针对当前请求,某个拦截器是否启用。

  • 默认不写配置项或者标为 true,都是开启。
  • 明确指定配置项为 false,才是关闭。

注意:在具体方法上配置时,没有明确配置为 true 时,可能没有开启功能。因为在实例化时的 config 对象上,可能将该功能设为 false 了。整个 axios 实例上关闭了该功能,则此时具体方法上必须明确设为 true 覆盖掉实例化的配置才能启用。

以当前请求是否开启响应反馈为例:

typescript 复制代码
// index.ts

import HttpRequest from "./http/request";

// 实例化 axios
const httpRequest = new HttpRequest({
    baseURL: import.meta.env.VITE_APP_API_BASE_URL,
    timeout: import.meta.env.VITE_APP_API_TIMEOUT,
    // 开启响应反馈拦截器功能(注册拦截器后,默认就是开启)
    showMessage: true 
});

// 在该 axios 实例上注册请求响应反馈拦截器
httpRequest.getInstance().interceptors.response.use(responseMessageOnFulfilled, null);
typescript 复制代码
// 默认开启当前请求的请求响应弹窗反馈
async function handleMessageOpen() {
    const res = await httpRequest.get({
        url: "/mock/test"
    });
    console.log("res", res);
}

// 手动关闭当前请求的响应反馈
async function handleMessageClose() {
    const res = await httpRequest.get({
        url: "/mock/test",
        showMessage: false
    });
    console.log("res", res);
}

扩展 config 类型

给 axios config 对象添加额外配置项,首先得扩充它的类型AxiosRequestConfig

typescript 复制代码
// 开关的默认值(默认都是开启)
const DEFAULT_EXTRA_FEATURE_CONFIG = { showMessage: true };

/** 扩展 axios 的请求配置类型 */
export interface MyAxiosRequestConfig<TReqBodyData = any> extends AxiosRequestConfig<TReqBodyData> {
    showMessage?: boolean; // 是否开启请求反馈提示框
}

拦截器中也需要用到 config 对象。但是在最新的 axios 中,请求拦截器中 config 的类型不再是 AxiosRequestConfig,而是InternalAxiosRequestConfig。因此还需要扩展一下请求拦截器 configInternalAxiosRequestConfig类型。

typescript 复制代码
/** 给拦截器使用 */
export interface MyInternalAxiosRequestConfig extends InternalAxiosRequestConfig {
    showMessage?: boolean;
}

有些功能在响应拦截器 onFulfilled 回调中完成,如响应反馈,也有些在 onRejected 回调中完成,如请求重试。因此也需要扩展响应拦截器中的回调,AxiosResponseAxiosError里的 config 对象类型。

typescript 复制代码
export interface MyAxiosResponse extends AxiosResponse {
    config: MyInternalAxiosRequestConfig;
}

export interface MyAxiosError extends AxiosError {
    config: MyInternalAxiosRequestConfig;
}

将额外配置项注入响应拦截器

实现各种功能的拦截器,可能是请求拦截器,也可能是响应拦截器。请求拦截器中可以很轻易拿到具体方法 config 上填写的配置项。但是响应拦截器却不好拿,因为 AxiosResponse 实例上的 config 对象是新生成的,它没有我们额外补充的配置项。

因此,我们需要在请求拦截器中先手动保存用户填写的配置项的值,然后在响应拦截器中手动添加到 config 对象上。并且该响应拦截器必须是最先执行的响应拦截器。

typescript 复制代码
class HttpRequest {
    private readonly instance: AxiosInstance;
		private readonly extraConfig: Record<string, boolean> = DEFAULT_EXTRA_FEATURE_CONFIG;

    constructor(config: MyAxiosRequestConfig) {
      	this.instance = axios.create(config);
    
        // 记录 config 额外配置项
        this.instance.interceptors.request.use((config: MyInternalAxiosRequestConfig) => {
            Object.keys(this.extraConfig).forEach(item => {
                if (config[item as keyof MyInternalAxiosRequestConfig] === false)
                    this.extraConfig[item] = !!config[item as keyof MyInternalAxiosRequestConfig];
            });
            return config;
        });
        // 将配置项补充到响应的 config 对象上
        this.instance.interceptors.response.use(
            (res: any) => {
                Object.keys(this.extraConfig).forEach(item => {
                    res.config[item] = this.extraConfig[item];
                    this.extraConfig[item] = true; // 配置复原成默认开启
                });
                return res;
            },
            (err: any) => {
                Object.keys(this.extraConfig).forEach(item => {
                    err.config[item] = this.extraConfig[item];
                    this.extraConfig[item] = true; // 配置复原成默认开启
                });
                throw err;
            }
        );
    }

    ...
}

请求响应反馈拦截器

typescript 复制代码
// src\api\http\message.ts

import { MyAxiosResponse } from "./request";

// 假设接口:{code: number, data: any, msg: string}
function showMessage(res: MyAxiosResponse) {
    const { data } = res;
    // alert 消息提示
    if (data.code >= 200 && data.code < 300) alert(data.msg);
    return res;
}

/**
 * 响应业务消息提示
 */
export function responseMessageOnFulfilled(res: MyAxiosResponse) {
    if (res.config.showMessage) showMessage(res); // showMessage 为 true,才给出反馈
    return res;
}

使用示例

vue 复制代码
<template>
  <div>
    <button @click="handleMessageOpen">message消息提示开</button>
    <button @click="handleMessageClose">message消息提示关</button>
  </div>
</template>

<script setup lang="ts">
  import HttpRequest from "./http/request";
  import { responseMessageOnFulfilled } from "./http/message";

	// 实例化
  const httpRequest = new HttpRequest({
    baseURL: "/",
    timeout: 10000
  });

  // 注册拦截器
  httpRequest.getInstance().interceptors.response.use(responseMessageOnFulfilled);

  // 反馈默认开
  async function handleMessageOpen() {
    const res = await httpRequest.get({
      url: "/__api/mock/get_test"
      // showMessage: true
    });
    console.log("res", res);
  }

  // 手动关闭反馈
  async function handleMessageClose() {
    const res = await httpRequest.get({
      url: "/__api/mock/get_test",
      showMessage: false
    });
    console.log("res", res);
  }
</script>

结果:

总结

通过在 config 对象上添加额外配置的方式来实现针对某个请求开关某个拦截器的目标。

  1. 扩展 config 类型
  2. 将额外配置项从请求 config 保存到 响应 config 对象上
  3. 编写具体功能的拦截器
相关推荐
咖啡无伴侣2 小时前
1. 从零搭建企业级 Monorepo 工程化模板:初始化、应用创建与共享 TypeScript 配置
前端·前端框架
moonsims2 小时前
再议AiBrainBox-V的前左右三目布局-满足多目SLAM算法;对比单下视VIO(低空、地面纹理丰富、飞行速度适中无人机 )&多目VIO
前端·人工智能·量子计算
AI编程实验室2 小时前
Agent Handoff 自动跟踪 Skill:安装、事件映射与证据分级
前端·ai编程
KoPa2 小时前
HeySmart:事件总线——异步解耦的艺术
前端·后端
金花顺2 小时前
ndroid 音频系统:AudioTrack 源码深度解析(从 Java 构造到 Native 启动)
前端·架构
用户921080262862 小时前
AI SSE Client 和普通 SSE Client 有什么不同:一次生成任务背后的坑与设计边界
前端
PedroQue992 小时前
@meng-xi/create-uni-app v1.0.0 正式发布
前端·uni-app
younuo36552 小时前
广州网站搭建费用明细:域名、服务器与开发成本全解析
服务器·前端·github
恋猫de小郭2 小时前
Flutter A2UI 深度解析,它是怎么提供动态生产力的,然后为什么 A2UI 不只是 Flutter
android·前端·flutter
snow@li3 小时前
服务器运维:Alibaba Cloud Linux 4 LTS・Vue前端 Java 后端 K3S 部署 CICD 目录规范清单
linux·前端·vue.js