7.3 KiB
7.3 KiB
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 调用方式错误
错误代码:
const response = await apiRequest<BackendRoutesResponse>({
method: 'GET',
url: '/rbac/user/routes', // ❌ 错误:将对象作为第一个参数
headers: {
'Authorization': `Bearer ${jwt}`
}
});
问题:
apiRequest函数的第一个参数应该是endpoint字符串- 我却传递了一个包含
method、url、headers的对象 - 导致
buildUrl函数接收到对象而不是字符串,调用endpoint.startsWith时报错
2. 响应数据访问方式错误
apiRequest 函数签名:
export async function apiRequest<T>(
endpoint: string, // 第一个参数:端点路径
options: ExtendedAxiosRequestConfig, // 第二个参数:配置对象
params?: QueryParams // 第三个参数:查询参数(可选)
): Promise<ApiResponse<T>>
返回值类型:
type ApiResponse<T> = {
data?: T; // 后端返回的完整响应对象
error?: string; // 错误信息
status: number; // HTTP 状态码
headers?: Record<string, string>;
};
后端返回格式:
{
"code": 0,
"msg": "成功",
"data": {
"user_id": 5,
"username": "admin",
"routes": [...]
}
}
数据访问层级:
response.data // 整个后端响应对象 { code, msg, data }
response.data.code // 业务状态码
response.data.msg // 提示信息
response.data.data // 实际数据 { user_id, username, routes }
response.data.data.routes // 路由数组
✅ 修复方案
修复 1:正确调用 apiRequest
修复后代码:
const response = await apiRequest<BackendRoutesResponse>(
'/rbac/user/routes', // ✅ 第一个参数:endpoint 字符串
{
method: 'GET',
headers: {
'Authorization': `Bearer ${jwt}`
}
} // ✅ 第二个参数:配置对象
);
关键点:
- 第一个参数:端点路径字符串(如
'/rbac/user/routes') - 第二个参数:配置对象(包含 method、headers 等)
- 第三个参数:查询参数对象(可选)
修复 2:正确访问响应数据
修复后代码:
// 检查响应是否成功
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<BackendRoutesResponse>(endpoint, options)
↓
axios.request(config)
↓
后端响应 (HTTP 200)
↓
response.data = {
code: 0,
msg: "成功",
data: {
user_id: 5,
username: "admin",
routes: [...]
}
}
↓
ApiResponse<BackendRoutesResponse> = {
data: { ← response.data 就是整个后端响应
code: 0,
msg: "成功",
data: {
user_id: 5,
username: "admin",
routes: [...]
}
},
status: 200,
headers: {...}
}
🧪 测试验证
1. 清除缓存
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] 用户路由权限加载成功: [...]
如果仍然报错,检查:
- 后端接口
/rbac/user/routes是否正常运行 - JWT token 是否有效
- 后端返回的数据格式是否正确
📁 修改的文件
app/api/auth/user-routes.ts
修改位置:getUserRoutesByRole 函数
修改内容:
- 修正
apiRequest调用方式(第一个参数是 endpoint 字符串) - 修正响应数据访问方式(通过
response.data.data.routes访问路由数组)
Git Diff:
- const response = await apiRequest<BackendRoutesResponse>({
- method: 'GET',
- url: '/rbac/user/routes',
- headers: { 'Authorization': `Bearer ${jwt}` }
- });
+ const response = await apiRequest<BackendRoutesResponse>(
+ '/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 应该报错
apiRequest<BackendRoutesResponse>({
method: 'GET',
url: '/rbac/user/routes'
});
// ❌ Argument of type '{ method: string; url: string; }' is not assignable to parameter of type 'string'
4. 添加详细的调试日志
在关键步骤添加日志,方便定位问题:
console.log('🔍 [User Routes] 后端返回:', response);
console.log('📋 [User Routes] 路由数组:', routes);
⚠️ 注意事项
-
axios 拦截器会自动添加 Authorization 头
- 从 localStorage 读取
access_token - 但为了确保使用正确的 token,这里仍然显式传递
- 从 localStorage 读取
-
后端接口必须返回标准格式
code: 业务状态码(0 或 200 表示成功)msg: 提示信息data: 实际数据(包含routes数组)
-
Element UI 图标映射
- 后端返回的是 Element UI 图标(如
el-icon-s-home) - 前端会自动转换为 RemixIcon(如
ri-home-line)
- 后端返回的是 Element UI 图标(如
修复完成时间: 2025-11-17 修复者: Claude Code