以配置的方式开关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. 编写具体功能的拦截器
相关推荐
于慨20 小时前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz20 小时前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
从前慢丶20 小时前
前端交互规范(Web 端)
前端
CHU72903520 小时前
便捷约玩,沉浸推理:线上剧本杀APP功能版块设计详解
前端·小程序
GISer_Jing20 小时前
Page-agent MCP结构
前端·人工智能
王霸天21 小时前
💥别再抄网上的Scale缩放代码了!50行源码教你写一个永不翻车的大屏适配
前端·vue.js·数据可视化
小领航21 小时前
用 Three.js + Vue 3 打造炫酷的 3D 行政地图可视化组件
前端·github
@大迁世界21 小时前
2026年React大洗牌:React Hooks 将迎来重大升级
前端·javascript·react.js·前端框架·ecmascript
PieroPc21 小时前
一个功能强大的 Web 端标签设计和打印工具,支持服务器端直接打印到局域网打印机。Fastapi + html
前端·html·fastapi
悟空瞎说21 小时前
深入 Vue3 响应式:为什么有的要加.value,有的不用?从设计到源码彻底讲透
前端·vue.js