前端学习 vben 之 axios interceptors

前端学习 vben 之 axios interceptors

interceptor

拦截器,是一种软件设计模式,核心思想就是在程序执行的特定阶段(如请求发送前,响应返回后,方法调用前后等)自动插入自定义逻辑。实现对核心流程的"拦截"和增强。它本质上是一种面向切面编程(AOP) 的具体实现,用于解耦横跨多个模块的通用功能(如日志、认证、错误处理)。

axios 中的 interceptor

axios 中实现了 interceptor,可以在发送请求前拦截请求,对请求进行一些处理,比如添加 token,添加请求头等。 在响应返回之后,也可以对响应进行一些处理,比如将响应数据转换成 json 对象,或者处理错误等。

vben 中的 axios 的 interceptor

vben 中预设了三个拦截器,文件地址packages\effects\request\src\request-client\preset-interceptors.ts

请求拦截器

请求拦截器,只写了一些设置请求头 token 的逻辑,和客户端所偏好的自然语言。

typescript 复制代码
config => {
  const accessStore = useAccessStore();

  config.headers.Authorization = formatToken(accessStore.accessToken);
  config.headers["Accept-Language"] = preferences.app.locale;
  return config;
};

响应拦截器

defaultResponseInterceptor

首先,我们要知道什么时候会触发 axios 请求成功的响应拦截器。

js 复制代码
  // `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。
  // 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),
  // 则promise 将会 resolved,否则是 rejected。
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 默认值
  },

可知,当 http status 为 200-300 之间时,才会触发请求响应的成功拦截器。

让我们看一下 axios 响应数据的格式

json 复制代码
{
  // `data` 由服务器提供的响应
  "data": {},

  // `status` 来自服务器响应的 HTTP 状态码
  "status": 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  "statusText": "OK",

  // `headers` 是服务器响应头
  // 所有的 header 名称都是小写,而且可以使用方括号语法访问
  // 例如: `response.headers['content-type']`
  "headers": {},

  // `config` 是 `axios` 请求的配置信息
  "config": {},

  // `request` 是生成此响应的请求
  // 在node.js中它是最后一个ClientRequest实例 (in redirects),
  // 在浏览器中则是 XMLHttpRequest 实例
  "request": {}
}

下面是defaultResponseInterceptor简化代码

ts 复制代码
type defaultResponseInterceptor = ({
  codeField,
  dataField,
  successCode,
}: {
  // 响应数据中,结果的字段名,用来判断请求是否成功
  codeField: string;
  // 响应数据中,结果数据字段名,或者自定义解析
  dataField: ((response: any) => any) | string;
  // 响应数据中,自定义响应成功的code
  successCode: ((code: any) => boolean) | number | string;
}) => {
  fulfilled;
};

执行defaultResponseInterceptor 返回一个对象,有 fulfilled 方法。

dataField

这个是用来指定返回 data 中实际需要的数据字段。

js 复制代码
// raw ,body , data
config.responseReturn === "body";

在请求的 config 中,vben 自定义添加了responseReturn字段,取值有三种,

  1. raw:返回原始数据
  2. body:返回 data 字段
  3. data:返回 data 字段中的数据
codeField

codeField 是指 上面 data 中的 code 字段。一般情况下,公司拥有自己一套请求成功还是失败的一个判断,基本都与业务相关联。比如请求字段缺少,请求字段不合法等等,还有与业务相关的情况,比如订单生成失败的 code 等。

successCode

用来判断响应的数据,是请求成功的,还是请求失败。

在 vben 中的 config,一般使用的是responseReturn = "data"。所以在页面使用的时候,请求函数中最终得到的响应结果是 data 字段中的数据。

errorMessageResponseInterceptor
ts 复制代码
type errorMessageResponseInterceptor = (makeErrorMessage: (message: string, error: any) => void) => { rejected };

调用makeErrorMessage方法,返回一个对象,对象中有 rejected 方法。 makeErrorMessage 方法中,处理了取消请求,请求超时,以及其他服务器返回的错误情况。如果对于请求成功,但是业务错误的情况,可以在请求成功响应拦截器中进行throw new Error(resp),然后通过makeErrorMessage,自定义处理这中情况。

ts 复制代码
export const defaultResponseInterceptor = ({
  codeField = "code",
  dataField = "data",
  successCode = 0,
}: {
  /** 响应数据中代表访问结果的字段名 */
  codeField: string;
  /** 响应数据中装载实际数据的字段名,或者提供一个函数从响应数据中解析需要返回的数据 */
  dataField: ((response: any) => any) | string;
  /** 当codeField所指定的字段值与successCode相同时,代表接口访问成功。如果提供一个函数,则返回true代表接口访问成功 */
  successCode: ((code: any) => boolean) | number | string;
}): ResponseInterceptorConfig => {
  return {
    fulfilled: response => {
      const { config, data: responseData, status } = response;

      if (config.responseReturn === "raw") {
        return response;
      }

      if (status >= 200 && status < 400) {
        if (config.responseReturn === "body") {
          return responseData;
        } else if (isFunction(successCode) ? successCode(responseData[codeField]) : responseData[codeField] === successCode) {
          return isFunction(dataField) ? dataField(responseData) : responseData[dataField];
        }
      }
      throw Object.assign({}, response, { response });
    },
  };
};

export const errorMessageResponseInterceptor = (makeErrorMessage?: MakeErrorMessageFn): ResponseInterceptorConfig => {
  return {
    rejected: (error: any) => {
      if (axios.isCancel(error)) {
        return Promise.reject(error);
      }

      const err: string = error?.toString?.() ?? "";
      let errMsg = "";
      if (err?.includes("Network Error")) {
        errMsg = $t("ui.fallback.http.networkError");
      } else if (error?.message?.includes?.("timeout")) {
        errMsg = $t("ui.fallback.http.requestTimeout");
      }
      if (errMsg) {
        makeErrorMessage?.(errMsg, error);
        return Promise.reject(error);
      }

      let errorMessage = "";
      const status = error?.response?.status;

      switch (status) {
        case 400: {
          errorMessage = $t("ui.fallback.http.badRequest");
          break;
        }
        case 401: {
          errorMessage = $t("ui.fallback.http.unauthorized");
          break;
        }
        case 403: {
          errorMessage = $t("ui.fallback.http.forbidden");
          break;
        }
        case 404: {
          errorMessage = $t("ui.fallback.http.notFound");
          break;
        }
        case 408: {
          errorMessage = $t("ui.fallback.http.requestTimeout");
          break;
        }
        default: {
          errorMessage = $t("ui.fallback.http.internalServerError");
        }
      }
      makeErrorMessage?.(errorMessage, error);
      return Promise.reject(error);
    },
  };
};
相关推荐
小飞悟15 分钟前
那些年我们忽略的高频事件,正在拖垮你的页面
javascript·设计模式·面试
绅士玖18 分钟前
📝 深入浅出 JavaScript 拷贝:从浅拷贝到深拷贝 🚀
前端
中微子28 分钟前
闭包面试宝典:高频考点与实战解析
前端·javascript
brzhang28 分钟前
前端死在了 Python 朋友的嘴里?他用 Python 写了个交互式数据看板,着实秀了我一把,没碰一行 JavaScript
前端·后端·架构
G等你下课1 小时前
告别刷新就丢数据!localStorage 全面指南
前端·javascript
该用户已不存在1 小时前
不知道这些工具,难怪的你的Python开发那么慢丨Python 开发必备的6大工具
前端·后端·python
爱编程的喵1 小时前
JavaScript闭包实战:从类封装到防抖函数的深度解析
前端·javascript
LovelyAqaurius1 小时前
Unity URP管线着色器库攻略part1
前端
Xy9101 小时前
开发者视角:App Trace 一键拉起(Deep Linking)技术详解
java·前端·后端
lalalalalalalala1 小时前
开箱即用的 Vue3 无限平滑滚动组件
前端·vue.js