jeecgboot vue API 拆分02

项目使用 Axios 作为 HTTP 请求库,并进行了封装。

封装类 : VAxios

import axios from 'axios';

src/

├── utils/http/axios/ ← 第1层:Axios 核心封装(请求引擎)

│ ├── Axios.ts ← VAxios 类:真正的请求器

│ ├── index.ts ← transform 拦截器 + defHttp 导出

│ ├── helper.ts ← 时间戳、日期格式化等工具

│ ├── checkStatus.ts ← HTTP 状态码处理

│ └── axiosCancel.ts ← 取消重复请求

├── api/ ← 第2层:业务 API 定义(按模块拆分)

│ ├── sys/ ← 系统相关(用户、权限、菜单)

│ │ ├── user.ts ← 登录/用户信息 API

│ │ ├── menu.ts ← 菜单 API

│ │ ├── upload.ts ← 上传 API

│ │ └── model/ ← TypeScript 类型定义

│ │ ├── userModel.ts

│ │ ├── menuModel.ts

│ │ └── uploadModel.ts

│ │

│ ├── demo/ ← 示例模块

│ │ ├── account.ts

│ │ ├── table.ts

│ │ └── model/

│ │

│ ├── common/ ← 公共 API

│ │ └── api.ts

│ │

│ └── model/ ← 通用 Model(分页基础类型)

│ └── baseModel.ts

└── views/xxx/ ← 第3层:页面调用(import API → 使用)

└── sys/login/useLogin.ts ← 例如:登录页调用 loginApi()

utils/http/axios/

AXios使用

java 复制代码
// utils/http/axios/http.js
import axios from 'axios';

// 创建 axios 实例
const http = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com', // 示例接口
  timeout: 5000, // 请求超时
  headers: {
    'Content-Type': 'application/json'
  }
});

// 请求拦截器(可选)
http.interceptors.request.use(
  config => {
    // 在发送请求前可以做一些处理,例如添加 token
    // config.headers.Authorization = `Bearer ${token}`;
    return config;
  },
  error => Promise.reject(error)
);

// 响应拦截器(可选)
http.interceptors.response.use(
  response => response.data, // 直接返回 data
  error => Promise.reject(error)
);

export default http;

在业务组件或 API 模块中,直接引入上述封装好的实例即可发起请求:

java 复制代码
// 调用示例
import http from '@/utils/http/axios/http';

// GET 请求
http.get('/posts/1')
  .then(res => {
    console.log('GET response:', res);
  })
  .catch(err => {
    console.error('GET error:', err);
  });

// POST 请求
http.post('/posts', {
  title: 'foo',
  body: 'bar',
  userId: 1
})
  .then(res => {
    console.log('POST response:', res);
  })
  .catch(err => {
    console.error('POST error:', err);
  });

requestservice 只是两个局部变量名 。它们指向的是内存中同一个 Axios 实例对象。所以,无论你叫 requestaxiosInstance 还是 http,在功能上都是完全等价的。

java 复制代码
import service from '@/utils/http/axios'; 

export function getUserInfo(id) {
  return service({ url: `/user/${id}`, method: 'get' });
}

历史

最早网页提交数据是这样的:

复制代码
浏览器
 ↓
提交表单
 ↓
服务器
 ↓
返回新页面
 ↓
整个页面刷新

缺点:

  • 页面闪烁
  • 整页刷新
  • 用户体验差

第二阶段:AJAX(2005左右)

XMLHttpRequest XHR

var xhr = new XMLHttpRequest();

xhr.open("GET", "/user/info");

xhr.onreadystatechange = function () {

if(xhr.readyState === 4){

if(xhr.status === 200){

console.log(xhr.responseText);

}

}

};

xhr.send();
页面

XHR

服务器

返回JSON

局部更新页面

JQuery时代

$.ajax({

url:'/user/list',

type:'GET',

success:function(res){

console.log(res);

}

})

$.get('/user/list');

$.post('/login');

第三阶段:Promise时代

$.ajax({

success:function(){

$.ajax({

success:function(){

$.ajax({

})

}

})

}

})

大家开始讨厌回调地狱:

复制代码
fetch()
java 复制代码
fetch('/user/list')
  .then(res => res.json())
  .then(data => {
      console.log(data);
  });

const data = await fetch('/user/list');

Axios诞生

坑1:不会自动转JSON

const res = await axios.get('/user');

console.log(res.data);

Fetch:

复制代码
const res = await fetch('/user');

const data = await res.json();

多一步。

坑2:404不会报错

Axios:

复制代码
axios.get('/404')
.catch(err=>{
   console.log("错误");
})

Fetch:

复制代码
const res = await fetch('/404');

console.log(res.ok); // false

不会自动进入 catch。

需要自己判断:

复制代码
if(!res.ok){
   throw new Error();
}

坑3:没有请求拦截器

Axios:

复制代码
axios.interceptors.request.use(...)

Fetch:

复制代码
fetch(...)

没有。

你得自己封装。

坑4:没有响应拦截器

Axios:

复制代码
复制代码
axios.interceptors.response.use(...)

Fetch 没有。


坑5:取消请求复杂

Axios:

复制代码
CancelToken
AbortController

已经封装好了。


所以 Axios 本质是什么?

其实就是:

复制代码
Axios
=
XMLHttpRequest
+
Promise
+
拦截器
+
超时处理
+
自动JSON
+
取消请求
+
统一配置

1

复制代码
axios.get('/sys/user/list')
复制代码
2
复制代码
request({
    url:'/sys/user/list'
})

统一 baseURL。

复制代码
3
复制代码
requestInterceptors()
responseInterceptors()

统一 Token。

复制代码
4
复制代码
transformRequestHook()

统一处理:

复制代码
{
  "code":200,
  "message":"成功",
  "result":[]
}

变成:

复制代码
[]
复制代码
4
复制代码
AxiosCanceler

防重复提交。

复制代码
5
复制代码
VAxios

把所有逻辑集中管理。

封装

没有对axios基础了解。

VAxios 类 ( Axios.ts ):-

提供 get / post / put / delete / request 方法

  • 提供 uploadFile 文件上传方法

  • 通过构造函数接收 CreateAxiosOptions 配置

  • utils/http/axios/

    ├── axiosTransform.ts ← 类型定义(抽象基类 + 配置接口)

    ├── Axios.ts ← VAxios 核心类(请求引擎)

    ├── index.ts ← 业务 transform + defHttp 实例(对外入口)

    ├── helper.ts ← 时间戳、日期格式化小工具

    ├── checkStatus.ts ← HTTP 状态码统一处理

    └── axiosCancel.ts ← 重复请求取消管理器

axiosTransform.ts

java 复制代码
// 1) 扩展 axios 原生的 AxiosRequestConfig
export interface CreateAxiosOptions extends AxiosRequestConfig {
  authenticationScheme?: string;   // 鉴权方案,如 "Bearer"
  transform?: AxiosTransform;      // ★ 核心:钩子集合
  requestOptions?: RequestOptions; // 业务选项(是否转响应、是否弹错等)
}

// 2) 抽象钩子类:所有请求处理流程都在这里预留口子
export abstract class AxiosTransform {
  // 钩子①:请求发起前(拼前缀、转 data 为 query string) 箭头函数是返回值
  beforeRequestHook?: (config, options) => AxiosRequestConfig;

  // 钩子②:响应返回后(解包 result、根据 code 判成功)
  transformRequestHook?: (res, options) => any;

  // 钩子③:请求 Promise catch 时
  requestCatchHook?: (e, options) => Promise<any>;

  // 钩子④⑤:axios 原生请求/响应拦截器(加 Token、加签名)
  requestInterceptors?: (config, options) => AxiosRequestConfig;
  responseInterceptors?: (res) => AxiosResponse;

  // 钩子⑥⑦:拦截器 error 回调
  requestInterceptorsCatch?: (error) => void;
  responseInterceptorsCatch?: (error) => void;
}

设计亮点 : VAxios 只负责"调用钩子",不关心钩子内部做什么。因此你可以:

  • 做一个 defHttp 用 Token + 签名

  • 再做一个 thirdPartyHttp 用不同的 header 规则

  • 甚至做一个 mockHttp 用于单测

→ 一套引擎,多种实例。

封装

VAxios

在 TypeScript、JavaScript 的 class 中,当你 new 一个对象时,会自动执行 constructor

java 复制代码
constructor(options: CreateAxiosOptions) {
  this.options = options;                     // 保存配置
  this.axiosInstance = axios.create(options); // 创建原生 axios 实例
  this.setupInterceptors();                   // 安装拦截器
}

setupInterceptors --- 拦截器管道安装 ( Axios.ts#L69-L111 )

java 复制代码
/**
 * @description: 拦截器配置 ------ 这是整个请求流程的"管道安装器"
 *
 * 执行时机:在 VAxios 构造函数里被调用,只执行一次
 * 做的事:向 axios 原生实例的 interceptors.request / interceptors.response
 *            里分别注册 4 个"钩子",后续每个请求/响应都会按顺序经过它们
 */
private setupInterceptors() {
  const transform = this.getTransform();
  if (!transform) {
    return;
  }
 const {
    requestInterceptors,            // 请求拦截器(加 Token/签名/租户ID)
    requestInterceptorsCatch,      // 请求拦截器出错时
    responseInterceptors,          // 响应拦截器
    responseInterceptorsCatch,      // 响应拦截器出错时
  } = transform;
  // 【第二步:实例化"重复请求取消器"
  // AxiosCanceler 内部用 Map<method&url, cancelFn> 记录正在进行的请求
  // 同一个 method&url 的新请求到来时,会先 cancel 掉旧的,再加入新的
  // ───────────────────────────────────────────────
  const axiosCanceler = new AxiosCanceler();
this.axiosInstance.interceptors.request.use(...)

requestInterceptors(config){

config.headers.token = "123456";

return config;

}

原来请求:

复制代码
GET /user/list

经过拦截器:

复制代码
GET /user/list

token:123456

所以请求拦截器就是:

复制代码
发请求前修改配置

this.axiosInstance.interceptors.response.use(...)

这是响应拦截器。

服务器返回:

复制代码
{
  "code":200,
  "msg":"成功",
  "result":[]
}

进入:

复制代码
responseInterceptors(res){
  console.log("收到结果");
  return res;
}

作用:

复制代码
收到服务器数据后统一处理

// 响应结果拦截器错误捕获

responseInterceptorsCatch &&

isFunction(responseInterceptorsCatch) &&

this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);

1 创建请求配置

2 requestInterceptors

加token

3 发给后端

4 后端返回数据

5 responseInterceptors

统一处理结果

6 返回给页面

index.js

const transform: AxiosTransform = {就是"给 JeecgBoot 的 axios 封装灌入真正的业务逻辑" ------ 没有它, VAxios 就是一个空壳,所有 Hook 都不会做任何事。

}

Hook 名称 调用时机 作用 返回值 / 注意事项
beforeRequestHook 请求发送前 对请求的 config 做统一处理,比如拼接前缀、时间戳、格式化参数 必须返回处理后的 config
requestInterceptors 请求发送前(axios 拦截器层) 修改请求头、加 token、签名、租户ID等,或做低代码/共享租户处理 必须返回 config,最终会传给 axios 发送请求
requestInterceptorsCatch 请求拦截器报错时 捕获请求拦截器里抛出的异常 一般处理日志或提示,不返回值
transformRequestHook 响应返回后 对返回数据做统一处理,比如解包 result,提示消息,处理超时或失败 返回最终数据,如果抛异常会被 request 的 Promise reject
responseInterceptors 响应返回后(axios 拦截器层) 可以对原始响应做处理,比如修改返回结构、统一解包 返回处理后的 res
responseInterceptorsCatch 响应报错时 捕获响应异常(HTTP 401/500/网络错误等),记录日志,弹窗或提示消息 返回 Promise.reject(error)
  • 请求阶段beforeRequestHook → requestInterceptors → requestInterceptorsCatch
  • 响应阶段responseInterceptors → transformRequestHook → responseInterceptorsCatch
java 复制代码
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  // 1. 克隆一份 config,避免修改外部传入的对象
  let conf: CreateAxiosOptions = cloneDeep(config);

  // 2. 获取用户自定义的 transform 对象(里面包含各种 hook)
  const transform = this.getTransform();

  // 3. 获取 VAxios 构造时默认的 requestOptions
  const { requestOptions } = this.options;

  // 4. 合并默认选项和本次请求的自定义 options
  const opt: RequestOptions = Object.assign({}, requestOptions, options);

  // 5. 从 transform 中取出需要用的 hook
  const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {};

  // 6. 调用 beforeRequestHook 对请求 config 进行加工处理(比如加 token、url 拼接、参数处理)
  if (beforeRequestHook && isFunction(beforeRequestHook)) {
    conf = beforeRequestHook(conf, opt);
  }

  // 7. 把 opt 挂到 conf 上,供后续拦截器使用
  conf.requestOptions = opt;

  // 8. 如果是 form-data 数据,把 data 格式化成 URLSearchParams 或 FormData
  conf = this.supportFormData(conf);

  // 9. 返回一个 Promise,外部使用 await 或 then 接收结果
  return new Promise((resolve, reject) => {
    // 10. 真正调用 axios 发请求
    this.axiosInstance
      .request<any, AxiosResponse<Result>>(conf)
      .then((res: AxiosResponse<Result>) => {
        // 11. 请求成功,调用 transformRequestHook 对响应进行加工(比如解包 data、弹消息)
        if (transformRequestHook && isFunction(transformRequestHook)) {
          try {
            const ret = transformRequestHook(res, opt);

            // ★ 可选:支持原始 success 回调
            config.success && config.success(res.data);

            // 12. resolve 返回加工后的数据
            resolve(ret);
          } catch (err) {
            // 13. 处理 transformRequestHook 内部抛出的异常
            reject(err || new Error('request error!'));
          }
          return;
        }

        // 14. 如果没有 transformRequestHook,则直接返回原始响应(类型转换成 T)
        resolve(res as unknown as Promise<T>);
      })
      .catch((e: Error | AxiosError) => {
        // 15. 请求失败,调用 requestCatchHook 处理异常
        if (requestCatchHook && isFunction(requestCatchHook)) {
          reject(requestCatchHook(e, opt));
          return;
        }

        // 16. axios 自带错误处理,可以在这里重写 error 信息
        if (axios.isAxiosError(e)) {
          // 可以自定义错误信息,例如 e.message = "请求超时"
        }

        // 17. 最终 reject 异常给调用者
        reject(e);
      });
  });
}

使用

java 复制代码
/**
 * @description: user login api
 */
export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal') {
  return defHttp.post<LoginResultModel>(
    {
      url: Api.Login,
      params,
    },
    {
      errorMessageMode: mode,
    }
  );
}
java 复制代码
export interface LoginParams {
  username: string;
  password: string;
}
java 复制代码
enum Api {
  Login = '/sys/login',
  phoneLogin = '/sys/phoneLogin',
  Logout = '/sys/logout',
  GetUserInfo = '/sys/user/getUserInfo',
  // 获取系统权限
  // 1、查询用户拥有的按钮/表单访问权限
  // 2、所有权限
  // 3、系统安全模式
  GetPermCode = '/sys/permission/getPermCode',
  //新加的获取图形验证码的接口
  getInputCode = '/sys/randomImage',
  //获取短信验证码的接口
  getCaptcha = '/sys/sms',
  //注册接口
  registerApi = '/sys/user/register',
  //校验用户接口
  checkOnlyUser = '/sys/user/checkOnlyUser',
  //SSO登录校验
  validateCasLogin = '/sys/cas/client/validateLogin',
  //校验手机号
  phoneVerify = '/sys/user/phoneVerification',
  //修改密码
  passwordChange = '/sys/user/passwordChange',
  //第三方登录
  thirdLogin = '/sys/thirdLogin/getLoginUser',
  //第三方登录
  getThirdCaptcha = '/sys/thirdSms',
  //获取二维码信息
  getLoginQrcode = '/sys/getLoginQrcode',
  //监控二维码扫描状态
  getQrcodeToken = '/sys/getQrcodeToken',
}

post<T = any>VAxios 里的方法,带 泛型 T

post<LoginResultModel> 指定泛型 T 为 LoginResultModel,也就是说这个请求返回的结果会被当作 LoginResultModel 类型。

java 复制代码
  post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'POST' }, options);
  }

post<T = any>(

config: AxiosRequestConfig, // 第一个参数:请求配置

options?: RequestOptions // 第二个参数(可选):自定义请求选项

): Promise<T> // 返回值:泛型 Promise<T>

java 复制代码
/**
 * @description: Login interface return value
 */
export interface LoginResultModel {
  userId: string | number;
  token: string;
  role: RoleInfo;
  userInfo?: any
}

const data = await phoneLoginApi(loginParams, mode);

// 代码逻辑说明: 【issues/7488】手机号码登录,在请求头中无法获取租户id---

const { token , userInfo } = data;

相关推荐
赵谨言1 小时前
基于C#的在线编码与自动化测试全栈Web平台的设计与实现
开发语言·前端·c#
Raink老师1 小时前
【AI面试临阵磨枪-98】前端如何展示多模态流式输出:文字打字机 + 图片渐进 + 音频播放?
前端·人工智能·面试
AI_零食1 小时前
奶茶大数据运维表 - 鸿蒙PC Electron框架技术实现详解
运维·前端·华为·electron·开源·harmonyos·鸿蒙
小雨下雨的雨1 小时前
鸿蒙PC Electron框架实现流体气泡模拟器
前端·人工智能·算法·华为·electron·鸿蒙
ZC跨境爬虫1 小时前
跟着 MDN 学JavaScript day_4:如何存储你需要的信息——变量
开发语言·前端·javascript·ui·ecmascript
星栈独行1 小时前
10 分钟跑起第一个 Makepad 应用:先把窗口开起来
前端·程序人生·ui·rust·开源·github
独隅1 小时前
Chrome插件开发实战详细指南
前端·chrome
VcB之殇1 小时前
[Three.js] 实现两个3D模型之间的粒子化切换
前端·javascript·three.js
喵了几个咪1 小时前
技术复盘:基于 GoWind Admin 实现 Kratos 框架单体轻量化落地
前端·架构