以配置的方式开关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. 编写具体功能的拦截器
相关推荐
逐·風3 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫3 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦4 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子4 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享5 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
清灵xmf7 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
大佩梨7 小时前
VUE+Vite之环境文件配置及使用环境变量
前端
GDAL7 小时前
npm入门教程1:npm简介
前端·npm·node.js
小白白一枚1118 小时前
css实现div被图片撑开
前端·css