拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Vue Vben Admin 服务端交互与数据 Mock 实战:axios 请求封装、Token 刷新与 Nitro 本地 Mock 全指南

Vue Vben Admin 服务端交互与数据 Mock 实战axios 请求封装、Token 刷新与 Nitro 本地 Mock 全指南【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. Its fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin本篇技术指南围绕 Vue Vben Admin 的服务端交互与数据 Mock 展开系统讲解开发/生产环境下接口地址与跨域代理的配置方法、基于vben/request的 axios 请求封装与拦截器体系、刷新 Token 的完整链路以及基于 Nitro 的本地 Mock 服务器的原理与关闭方式。读者阅读完本文后将能够在 Vue Vben Admin 中独立配置多环境接口、定制请求客户端、开启无感刷新 Token并理解 Mock 服务的工作机制与源码实现。开发环境交互在开发阶段前端应用如apps/web-antd与后端接口服务器如果运行在不同主机上浏览器直接请求接口会出现跨域问题因此需要将接口请求通过 Vite 开发服务器代理到接口服务器如果前后端运行在同一个主机上则可以直接请求具体接口地址无需代理。本地开发跨域配置项目默认已经配置好了本地开发跨域如需调整可以按下面的步骤修改。配置本地开发接口地址在应用目录如apps/web-antd下的.env.development文件中配置接口地址默认配置为/api# apps/web-antd/.env.development VITE_GLOB_API_URL/api.env.development中的VITE_GLOB_*系列变量会在构建时注入应用配置详见后文生产环境交互最终通过useAppConfig读取并作为请求客户端的baseURL。配置开发服务器代理开发环境需要处理跨域时在对应应用目录下的vite.config.ts如 apps/web-antd/vite.config.ts中配置代理// apps/web-antd/vite.config.ts import { defineConfig } from vben/vite-config; export default defineConfig(async () { return { application: {}, vite: { server: { proxy: {// [!code focus:11] /api: { changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ), // mock代理目标地址 target: http://localhost:5320/api, ws: true, }, }, }, }, }; });各配置项说明changeOrigin修改请求头中的Host为目标地址解决跨域Origin校验问题rewrite把请求路径中的/api前缀去掉后再转发。例如/api/auth/login会转发为/auth/login配合下面的target最终指向http://localhost:5320/api/auth/logintarget代理目标地址默认指向本地 Nitro Mock 服务http://localhost:5320/apiws是否代理 WebSocket 连接开启后可用于ws协议的长连接。接口请求完成上述配置后前端项目即可使用/api作为接口请求前缀例如import axios from axios; axios .post(/api/auth/login, { username: vben, password: 123456 }) .then((res) { console.log(res); });此时请求会被代理到http://localhost:5320/api/auth/login。::: warning 注意 从浏览器控制台的 Network 面板看请求地址显示为http://localhost:5555/api/auth/login这是因为 proxy 配置并不会改变本地请求的 URL浏览器视角的地址仍是开发服务器地址端口取决于应用配置如apps/web-antd的 .env.development 中VITE_PORT5666实际转发目标才是代理配置中的target。 :::没有跨域时的配置如果前后端同源、不存在跨域问题可以忽略代理配置直接将接口地址写入VITE_GLOB_API_URL# apps/web-antd/.env.development VITE_GLOB_API_URLhttps://mock-napi.vben.pro/api这样所有以/api开头的请求都会直接请求该绝对地址不再经过 Vite 代理。生产环境交互接口地址配置生产环境在应用目录下的.env.production文件中配置接口地址# apps/web-antd/.env.production VITE_GLOB_API_URLhttps://mock-napi.vben.pro/api::: tip 打包后如何动态修改接口地址.env文件内VITE_GLOB_*开头的变量会在打包时注入dist/_app-config-{version}-{hash}.js文件内。部署时直接修改该文件中的接口地址并刷新页面即可无需针对不同环境多次打包一次打包可以复用于多个不同接口环境的部署。 :::跨域处理生产环境如果出现跨域问题可以使用 nginx 代理接口地址或者在后端开启 CORS。仓库中的 Mock 服务即通过 Nitro 的routeRules开启了 CORS见 apps/backend-mock/nitro.config.tsrouteRules: { /api/**: { cors: true, headers: { Access-Control-Allow-Credentials: true, Access-Control-Allow-Headers: Accept, Authorization, ..., Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE, Access-Control-Allow-Origin: *, }, }, },这是一个可参考的后端 CORS 处理示例对/api/**全部接口开放跨域允许携带凭证并暴露响应头。接口请求配置项目中默认自带基于 axios 封装的基础请求配置核心由vben/request包提供源码位于 packages/effects/request/src。项目没有做过度封装只是简单封装了常用配置其余需求可自行扩展。由于不同 app 使用的组件库与 store 不同各应用目录下都有对应的请求配置文件例如 apps/web-antd/src/api/request.ts、apps/web-naive/src/api/request.ts等可按需调整。请求客户端核心实现vben/request的核心是RequestClient类见 request-client.ts。构造时会对默认配置与传入配置做合并默认请求头Content-Type: application/json;charsetutf-8默认responseReturn: raw默认超时时间timeout: 10_00010 秒。同时RequestClient还内置了文件上传upload、文件下载download、SSE 长连接postSSE/requestSSE能力这些模块分别位于 uploader.ts、downloader.ts、sse.ts并有对应的单元测试如 uploader.test.ts、sse.test.ts。扩展的配置在 axios 基础配置之外vben/request扩展了两类配置类型定义见 types.tstype ExtendOptionsT any { /** * 参数序列化方式。预置了几种针对数组的序列化类型 * - brackets: ids[]1ids[]2ids[]3 * - comma: ids1,2,3 * - indices: ids[0]1ids[1]2ids[2]3 * - repeat: ids1ids2ids3 * default brackets */ paramsSerializer?: | brackets | comma | indices | repeat | AxiosRequestConfigT[paramsSerializer]; /** * 响应数据的返回方式。 * - raw: 原始的AxiosResponse包括headers、status等不做是否成功请求的检查。 * - body: 返回响应数据的BODY部分只会根据status检查请求是否成功忽略对code的判断这种情况下应由调用方检查请求是否成功。 * - data: 解构响应的BODY数据只返回其中的data节点数据会检查status和code是否为成功状态。 */ responseReturn?: body | data | raw; };其中paramsSerializer在 request-client.ts 中通过getParamsSerializer映射为qs库对应的arrayFormatbrackets/comma/indices/repeat实现数组参数序列化也可以直接传入 axios 原生的序列化函数。请求示例GET 请求import { requestClient } from #/api/request; export async function getUserInfoApi() { return requestClient.getUserInfo(/user/info); }POST/PUT 请求import { requestClient } from #/api/request; export async function saveUserApi(user: UserInfo) { return requestClient.postUserInfo(/user, user); } export async function updateUserApi(user: UserInfo) { return requestClient.putUserInfo(/user, user); } export async function saveOrUpdateUserApi(user: UserInfo) { const url user.id ? /user/${user.id} : /user/; return requestClient.requestUserInfo(url, { data: user, // 或者 PUT method: user.id ? PUT : POST, }); }DELETE 请求import { requestClient } from #/api/request; export async function deleteUserApi(userId: number) { return requestClient.deleteboolean(/user/${userId}); }get/post/put/delete方法最终都会收敛到通用的request方法见 request-client.ts统一由 axios 实例发起请求。请求配置以 apps/web-antd/src/api/request.ts 为例应用内的请求配置文件可以根据业务逻辑调整/** * 该文件可自行根据业务逻辑进行调整 */ import type { HttpResponse } from vben/request; import { useAppConfig } from vben/hooks; import { preferences } from vben/preferences; import { authenticateResponseInterceptor, errorMessageResponseInterceptor, RequestClient, } from vben/request; import { useAccessStore } from vben/stores; import { message } from ant-design-vue; import { useAuthStore } from #/store; import { refreshTokenApi } from ./core; const { apiURL } useAppConfig(import.meta.env, import.meta.env.PROD); function createRequestClient(baseURL: string) { const client new RequestClient({ baseURL, }); // 重新认证逻辑 async function doReAuthenticate() { console.warn(Access token or refresh token is invalid or expired. ); const accessStore useAccessStore(); const authStore useAuthStore(); accessStore.setAccessToken(null); if ( preferences.app.loginExpiredMode modal accessStore.isAccessChecked ) { accessStore.setLoginExpired(true); } else { await authStore.logout(); } } // 刷新token逻辑 async function doRefreshToken() { const accessStore useAccessStore(); const resp await refreshTokenApi(); const newToken resp.data; accessStore.setAccessToken(newToken); return newToken; } function formatToken(token: null | string) { return token ? Bearer ${token} : null; } // 请求头处理 client.addRequestInterceptor({ fulfilled: async (config) { const accessStore useAccessStore(); config.headers.Authorization formatToken(accessStore.accessToken); config.headers[Accept-Language] preferences.app.locale; return config; }, }); // 处理返回的响应数据格式。会根据responseReturn指定的类型返回对应的数据 client.addResponseInterceptor( defaultResponseInterceptor({ // 指定接口返回的数据中的 code 字段名 codeField: code, // 指定接口返回的数据中装载了主要数据的字段名 dataField: data, // 请求成功的 code 值如果接口返回的 code 等于 successCode 则会认为是成功的请求 successCode: 0, }), ); // token过期的处理 client.addResponseInterceptor( authenticateResponseInterceptor({ client, doReAuthenticate, doRefreshToken, enableRefreshToken: preferences.app.enableRefreshToken, formatToken, }), ); // 通用的错误处理,如果没有进入上面的错误处理逻辑就会进入这里 client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理根据不同的 code 做不同的提示而不是直接使用 message.error 提示 msg // 当前mock接口返回的错误字段是 error 或者 message const responseData error?.response?.data ?? {}; const errorMessage responseData?.error ?? responseData?.message ?? ; // 如果没有错误信息则会根据状态码进行提示 message.error(errorMessage || msg); }), ); return client; } export const requestClient createRequestClient(apiURL, { responseReturn: data, }); export const baseRequestClient new RequestClient({ baseURL: apiURL });上述代码暴露了两个客户端requestClient配置了完整拦截器链responseReturn为data业务代码直接拿到响应中的data节点baseRequestClient不带任何拦截器的裸客户端适合需要完全自行处理响应如raw方式的场景。三个预置拦截器defaultResponseInterceptor、authenticateResponseInterceptor、errorMessageResponseInterceptor的实现位于 preset-interceptors.tsdefaultResponseInterceptor按responseReturn分派响应——raw直接返回原始响应body仅按 HTTP 状态码校验后返回响应体data则进一步校验code字段是否等于successCode相等才解构返回dataField指定的数据节点否则抛出错误含response信息供上层取用authenticateResponseInterceptor仅在响应状态码为 401 时触发负责 Token 刷新与排队重试errorMessageResponseInterceptor把网络错误、超时以及 400/401/403/404/408 等状态码映射为国际化的提示文案如ui.fallback.http.networkError、ui.fallback.http.unauthorized并通过makeErrorMessage回调交给 UI 层展示。多个接口地址当需要同时对接多个后端服务时只需创建多个requestClient实例即可例如通过useAppConfig读取多个环境变量const { apiURL, otherApiURL } useAppConfig( import.meta.env, import.meta.env.PROD, ); export const requestClient createRequestClient(apiURL); export const otherRequestClient createRequestClient(otherApiURL);对应的otherApiURL同样来自.env.development/.env.production中的VITE_GLOB_OTHER_API_URL之类的VITE_GLOB_*变量。刷新 Token项目中默认提供了刷新 Token 的逻辑开启只需两步。第一步开启配置开关调整对应应用目录下的preferences.ts确保enableRefreshToken为true默认值为false见 packages/core/preferences/src/config.tsimport { defineOverridesPreferences } from vben/preferences; export const overridesPreferences defineOverridesPreferences({ // overrides app: { enableRefreshToken: true, }, });第二步配置刷新逻辑在应用目录的src/api/request.ts中确认doRefreshToken与formatToken的实现// 这里调整为你的token格式 function formatToken(token: null | string) { return token ? Bearer ${token} : null; } /** * 刷新token逻辑 */ async function doRefreshToken() { const accessStore useAccessStore(); // 这里调整为你的刷新token接口 const resp await refreshTokenApi(); const newToken resp.data; accessStore.setAccessToken(newToken); return newToken; }refreshTokenApi由应用自身实现如apps/web-antd/src/api/core下的刷新接口对应 Mock 端实现见 apps/backend-mock/api/auth/refresh.post.ts其流程为从 Cookie 中读取 refreshToken → 校验 → 重新生成 accessToken 并写回 Cookie。底层刷新机制authenticateResponseInterceptor见 preset-interceptors.ts实现了完整的无感刷新与并发排队机制仅当响应状态码为 401 时进入该逻辑其余错误直接抛出若未开启enableRefreshToken或该请求已经是重试请求__isRetryRequest标记则直接执行doReAuthenticate弹窗提示登录过期或强制登出若此刻已有请求正在刷新 Tokenclient.isRefreshing为true当前请求进入refreshTokenQueue队列待新 Token 生成后取出并携带新Authorization重放首个 401 请求会置isRefreshing true、标记__isRetryRequest true调用doRefreshToken获取新 Token然后放行队列中所有等待请求并重试自身若刷新失败清空队列并回调doReAuthenticate提示重新登录。该机制保证了高并发下多个 401 请求只会触发一次刷新调用避免 Token 刷新风暴。相关队列与状态定义在 request-client.ts。数据 MockMock 数据是前端开发过程中必不可少的一环是分离前后端开发的关键链路。通过预先与服务端约定接口模拟请求数据甚至逻辑能够让前端开发独立自主不被服务端开发进度阻塞。::: tip 生产环境 Mock 新版本不再支持生产环境 Mock请使用真实接口。 :::Nitro 使用项目使用 Nitro轻量级后端服务器可部署在任何地方作为本地 Mock 服务器。其原理是在本地额外启动一个真实的后端服务可以处理请求并返回数据。Mock 服务代码位于 apps/backend-mock 目录下无需手动启动已集成到项目中——只需在项目根目录运行pnpm dev运行成功后会通过internal/vite-config的 nitro 插件自动拉起 Mock 服务控制台打印http://localhost:5320/api访问该地址即可查看 Mock 服务。Mock 服务采用 Nitro 的目录约定式路由例如登录接口 apps/backend-mock/api/auth/login.post.ts刷新 Token 接口 apps/backend-mock/api/auth/refresh.post.ts用户信息接口 apps/backend-mock/api/user/info.ts。文件名后缀.get/.post/.put/.delete即对应 HTTP 方法defineEventHandler中实现业务逻辑返回的数据结构遵循{ code, data, message }约定见apps/backend-mock/utils/response.ts。Nitro 语法简单可以根据需求自行配置与开发具体可参考 Nitro 官方文档。关闭 Mock 服务Mock 的本质是一个真实的后端服务如果不需要可以在应用目录下的.env.development文件中配置VITE_NITRO_MOCKfalse关闭默认开启见 apps/web-antd/.env.development# apps/web-antd/.env.development VITE_NITRO_MOCKfalse关闭后开发环境的/api请求将由 Vite 代理直接转发到target配置的真实后端地址。小结本文围绕 Vue Vben Admin 的服务端交互链路从环境配置到源码实现给出了完整落地方案开发与生产环境的接口地址通过VITE_GLOB_API_URL与 Vite 代理统一管理请求层基于vben/request的RequestClient与三个预置拦截器完成鉴权、响应解构、错误提示刷新 Token 通过偏好开关与拦截器队列实现并发安全的无感刷新Mock 层则由 Nitro 提供与真实后端一致的开发体验。掌握这些配置与实现即可在真实业务中快速接入后端服务。【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. Its fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门