# RBAC 路由权限集成 - 修复说明 ## 🐛 问题描述 在集成 RBAC 路由权限时遇到以下错误: ``` TypeError: endpoint.startsWith is not a function at buildUrl (axios-client.ts:168:16) at apiRequest (axios-client.ts:275:17) ``` --- ## 🔍 问题原因 ### 1. `apiRequest` 调用方式错误 **错误代码**: ```typescript const response = await apiRequest({ method: 'GET', url: '/rbac/user/routes', // ❌ 错误:将对象作为第一个参数 headers: { 'Authorization': `Bearer ${jwt}` } }); ``` **问题**: - `apiRequest` 函数的第一个参数应该是 `endpoint` 字符串 - 我却传递了一个包含 `method`、`url`、`headers` 的对象 - 导致 `buildUrl` 函数接收到对象而不是字符串,调用 `endpoint.startsWith` 时报错 --- ### 2. 响应数据访问方式错误 **`apiRequest` 函数签名**: ```typescript export async function apiRequest( endpoint: string, // 第一个参数:端点路径 options: ExtendedAxiosRequestConfig, // 第二个参数:配置对象 params?: QueryParams // 第三个参数:查询参数(可选) ): Promise> ``` **返回值类型**: ```typescript type ApiResponse = { data?: T; // 后端返回的完整响应对象 error?: string; // 错误信息 status: number; // HTTP 状态码 headers?: Record; }; ``` **后端返回格式**: ```json { "code": 0, "msg": "成功", "data": { "user_id": 5, "username": "admin", "routes": [...] } } ``` **数据访问层级**: ```typescript response.data // 整个后端响应对象 { code, msg, data } response.data.code // 业务状态码 response.data.msg // 提示信息 response.data.data // 实际数据 { user_id, username, routes } response.data.data.routes // 路由数组 ``` --- ## ✅ 修复方案 ### 修复 1:正确调用 `apiRequest` **修复后代码**: ```typescript const response = await apiRequest( '/rbac/user/routes', // ✅ 第一个参数:endpoint 字符串 { method: 'GET', headers: { 'Authorization': `Bearer ${jwt}` } } // ✅ 第二个参数:配置对象 ); ``` **关键点**: - 第一个参数:端点路径字符串(如 `'/rbac/user/routes'`) - 第二个参数:配置对象(包含 method、headers 等) - 第三个参数:查询参数对象(可选) --- ### 修复 2:正确访问响应数据 **修复后代码**: ```typescript // 检查响应是否成功 if (response.error) { console.error('❌ [User Routes] API 请求失败:', response.error); return { success: false, error: response.error }; } // 检查响应数据 if (!response.data) { console.error('❌ [User Routes] 后端未返回数据'); return { success: false, error: "后端未返回数据" }; } const backendResponse = response.data; // 整个后端响应对象 // 检查业务状态码 if (backendResponse.code !== 0 && backendResponse.code !== 200) { console.error(`❌ [User Routes] 后端返回错误: ${backendResponse.msg}`); return { success: false, error: backendResponse.msg }; } // 访问实际数据 const routes = backendResponse.data.routes; // 路由数组 ``` **关键点**: - `response.data` → 后端返回的完整对象(包含 code、msg、data) - `backendResponse.code` → 业务状态码(0 或 200 表示成功) - `backendResponse.data.routes` → 路由数组 --- ## 📊 数据流图 ``` apiRequest(endpoint, options) ↓ axios.request(config) ↓ 后端响应 (HTTP 200) ↓ response.data = { code: 0, msg: "成功", data: { user_id: 5, username: "admin", routes: [...] } } ↓ ApiResponse = { data: { ← response.data 就是整个后端响应 code: 0, msg: "成功", data: { user_id: 5, username: "admin", routes: [...] } }, status: 200, headers: {...} } ``` --- ## 🧪 测试验证 ### 1. 清除缓存 ```javascript localStorage.clear() location.reload() ``` ### 2. 重新登录 使用测试账号登录(如 `000 / admin06111`) ### 3. 观察控制台日志 **预期输出**: ``` 🔍 [User Routes] 获取用户路由,角色: admin 🔍 [User Routes] 后端返回: { data: { code: 0, msg: "成功", ... }, status: 200 } ✅ [User Routes] 成功获取 29 个路由 📋 [User Routes] 菜单数据: [...] ✅ [Sidebar] 用户路由权限加载成功: [...] ``` **如果仍然报错**,检查: 1. 后端接口 `/rbac/user/routes` 是否正常运行 2. JWT token 是否有效 3. 后端返回的数据格式是否正确 --- ## 📁 修改的文件 ### `app/api/auth/user-routes.ts` **修改位置**:`getUserRoutesByRole` 函数 **修改内容**: 1. 修正 `apiRequest` 调用方式(第一个参数是 endpoint 字符串) 2. 修正响应数据访问方式(通过 `response.data.data.routes` 访问路由数组) **Git Diff**: ```diff - const response = await apiRequest({ - method: 'GET', - url: '/rbac/user/routes', - headers: { 'Authorization': `Bearer ${jwt}` } - }); + const response = await apiRequest( + '/rbac/user/routes', // endpoint (第一个参数) + { + method: 'GET', + headers: { 'Authorization': `Bearer ${jwt}` } + } // options (第二个参数) + ); - if (response.code !== 0 && response.code !== 200) { - // ... - } + const backendResponse = response.data; + if (backendResponse.code !== 0 && backendResponse.code !== 200) { + // ... + } - const routes = response.data.routes; + const routes = backendResponse.data.routes; ``` --- ## 💡 经验总结 ### 1. 仔细检查函数签名 在调用函数之前,务必确认: - 参数的数量和顺序 - 每个参数的类型 - 返回值的结构 ### 2. 理解数据嵌套结构 后端返回的数据通常有多层嵌套,需要逐层访问: ``` response (ApiResponse) └─ data (后端完整响应) ├─ code (业务状态码) ├─ msg (提示信息) └─ data (实际数据) └─ routes (路由数组) ``` ### 3. 使用 TypeScript 类型检查 如果正确定义了类型,TypeScript 会在编译时发现这类错误: ```typescript // 如果这样调用,TypeScript 应该报错 apiRequest({ method: 'GET', url: '/rbac/user/routes' }); // ❌ Argument of type '{ method: string; url: string; }' is not assignable to parameter of type 'string' ``` ### 4. 添加详细的调试日志 在关键步骤添加日志,方便定位问题: ```typescript console.log('🔍 [User Routes] 后端返回:', response); console.log('📋 [User Routes] 路由数组:', routes); ``` --- ## ⚠️ 注意事项 1. **axios 拦截器会自动添加 Authorization 头** - 从 localStorage 读取 `access_token` - 但为了确保使用正确的 token,这里仍然显式传递 2. **后端接口必须返回标准格式** - `code`: 业务状态码(0 或 200 表示成功) - `msg`: 提示信息 - `data`: 实际数据(包含 `routes` 数组) 3. **Element UI 图标映射** - 后端返回的是 Element UI 图标(如 `el-icon-s-home`) - 前端会自动转换为 RemixIcon(如 `ri-home-line`) --- **修复完成时间**: 2025-11-17 **修复者**: Claude Code