项目自定义路由开发指南
概述
项目路由(Project Routes)是一个灵活的插件机制,允许您在容器外为特定项目编写自定义 API 接口,无需修改系统核心代码。每个项目可以根据业务需求开发独立的 API 路由。
快速开始
1. 创建路由配置文件
在您的项目目录下创建 api/routes.js 文件:
projects/
└── your-project/
└── api/
├── routes.js # 路由配置(必需)
├── handlers.js # 业务处理函数
└── util.js # 工具函数
2. 定义路由
在 routes.js 中导出路由配置数组:
import handlers from './handlers.js';
export default [
{
method: 'POST', // HTTP 方法:GET、POST、PUT、DELETE
path: '/custom-api', // 路由路径
skipAuth: false, // 是否跳过系统认证(默认 false)
function: handlers.myHandler, // 处理函数
},
{
method: 'GET',
path: '/public-api',
skipAuth: true, // 跳过认证,公开访问
function: (req, res) => {
return res.json({ message: 'Hello World' });
},
},
];
3. 实现处理函数
在 handlers.js 中实现业务逻辑:
import DBConnector from '../../../src/db-connector.js';
const myHandler = async (req, res, next) => {
try {
// 获取请求参数
const { id } = req.body;
// 访问数据库
const result = await DBConnector.collection('myCollection')
.findOne({ _id: id });
// 返回响应
return res.json({
success: true,
data: result
});
} catch (error) {
// 错误处理
return res.status(500).json({
success: false,
message: error.message
});
}
};
export default {
myHandler,
};
路由配置详解
路由对象属性
| 属性 | 类型 | 必需 | 说明 |
|---|---|---|---|
method | String | 是 | HTTP 方法:GET、POST、PUT、DELETE |
path | String | 是 | 路由路径,如 /custom-api |
function | Function | 是 | Express 风格的处理函数 (req, res, next) => {} |
skipAuth | Boolean | 否 | 是否跳过系统 JWT 认证,默认 false |
auth | Function | 否 | 自定义认证中间件(会自动跳过系统认证) |
访问路径
您定义的路由会自动挂载到两个 URL 路径:
-
项目特定路径:
/api/{项目名称}{path}- 示例:
/api/my-project/custom-api
- 示例:
-
通用项目路径:
/api/project{path}- 示例:
/api/project/custom-api
- 示例:
认证选项
使用系统认证(默认)
{
method: 'POST',
path: '/protected-api',
function: handler, // 用户必须登录并携带有效 JWT token
}
跳过认证(公开接口)
{
method: 'GET',
path: '/public-api',
skipAuth: true,
function: handler, // 任何人都可以访问
}
自定义认证
import myAuth from './auth.js';
export default [
{
method: 'POST',
path: '/custom-auth-api',
auth: myAuth, // 使用自定义认证逻辑
function: handler,
}
];
自定义认证中间件示例(auth.js):
export default async (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (apiKey !== 'your-secret-key') {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
};
实战示例
以下是一个完整的实际应用示例,展示了如何实现单点登录(SSO)和数据查询接口。
routes.js
import login from './login.js';
export default [
{
method: 'POST',
path: '/getOneAccount',
skipAuth: true,
function: login.getOneAccount,
},
{
method: 'GET',
path: '/home',
skipAuth: true,
function: login.sso,
},
{
method: 'GET',
path: '/heartbeat',
skipAuth: true,
function: (req, res) => {
return res.send('success');
},
},
];
login.js
import DBConnector from '../../../src/db-connector.js';
import axios from 'axios';
import { signToken } from '../../../src/api/v1/auth.js';
// 查询账户信息
const getOneAccount = async (req, res, next) => {
try {
const EMPLOYEE_ID = req.body.EMPLOYEE_ID;
if (!EMPLOYEE_ID) {
throw new Error('必须提供 EMPLOYEE_ID 参数');
}
// 查询数据库
const account = await DBConnector.collection('accounts')
.find({ username: EMPLOYEE_ID })
.limit(1)
.toArray();
// 移除敏感信息
const result = account.map((a) => ({
...a,
password: undefined
}));
return res.json({
success: true,
data: result,
total: account.length,
});
} catch (error) {
return res.status(500).json({
success: false,
message: error.message
});
}
};
// 单点登录
const sso = async (req, res, next) => {
try {
const { usertoken } = req.query;
if (!usertoken) {
throw new Error('必须提供 usertoken 参数');
}
// 调用第三方认证服务
const response = await axios.post('https://auth.example.com/verify', {
token: usertoken
});
const { employeeId } = response.data;
// 查询本地账户
const account = await DBConnector.collection('accounts')
.findOne({ username: employeeId });
if (!account) {
throw new Error('用户不存在');
}
// 签发系统 JWT token
const token = await signToken(account);
// 重定向到首页
const redirectURI = '/dashboard?headerRender=0';
return res.redirect(`/user/login?token=${token}&redirect=${encodeURIComponent(redirectURI)}`);
} catch (error) {
return res.status(500).json({
success: false,
message: error.message
});
}
};
export default {
getOneAccount,
sso,
};
可用资源
数据库访问
import DBConnector from '../../../src/db-connector.js';
// MongoDB 操作
const data = await DBConnector.collection('accounts').find({}).toArray();
// 更新
await DBConnector.collection('accounts').updateOne(
{ _id: id },
{ $set: { status: 'active' } }
);
JWT Token 操作
import { signToken, verifyToken } from '../../../src/api/v1/auth.js';
// 签发 token
const token = await signToken(userObject);
// 验证 token
const decoded = verifyToken(token);
租户隔离
系统会自动处理多租户隔离,您可以通过以下方式获取当前租户:
const tenant = req.headers.tenant;
请求对象(req)
req.body // POST/PUT 请求体
req.query // URL 查询参数
req.params // 路由参数
req.headers // HTTP 请求头
req.files // 上传的文件(支持 multipart/form-data)
req.uid // 当前用户 ID(如果已认证)
req.ip // 客户端 IP 地址
响应对象(res)
res.json({ data: 'value' }) // 返回 JSON
res.send('text') // 返回文本
res.status(404).json({ error: 'Not found' }) // 设置状态码
res.redirect('/path') // 重定向
res.sendFile('/path/to/file') // 发送文件
中间件执行顺序
您的路由会经过以下中间件链:
- 租户会话中间件 - 处理多租户隔离
- Referer 验证 - 如果启用(可选)
- 自定义认证中间件 - 如果提供
auth属性 - 系统认证 - 如果未设置
skipAuth: true - 您的处理函数 - 最终执行业务逻辑
最佳实践
1. 错误处理
始终使用 try-catch 捕获异常:
const handler = async (req, res, next) => {
try {
// 业务逻辑
} catch (error) {
console.error('Error:', error);
return res.status(500).json({
success: false,
message: error.message
});
}
};
2. 参数验证
验证必需参数:
const handler = async (req, res) => {
const { userId, action } = req.body;
if (!userId || !action) {
return res.status(400).json({
error: 'Missing required parameters: userId, action'
});
}
// 继续处理...
};
3. 模块化
将代码拆分为多个模块:
api/
├── routes.js # 路由配置
├── handlers/ # 处理函数目录
│ ├── user.js
│ ├── data.js
│ └── report.js
├── middleware/ # 自定义中间件
│ └── auth.js
└── utils/ # 工具函数
├── validator.js
└── crypto.js
4. 日志记录
记录关键操作:
const handler = async (req, res) => {
console.log(`[${new Date().toISOString()}] API called:`, {
path: req.path,
method: req.method,
user: req.uid,
ip: req.ip
});
// 业务逻辑...
};
5. 安全注意事项
- 永远不要在响应中返回密码等敏感信息
- 对用户输入进行验证和清理
- 使用参数化查询防止注入攻击
- 公开接口应实施速率限制
// ✅ 安全:移除密码字段
const user = await DBConnector.collection('accounts').findOne({ _id: id });
delete user.password;
return res.json(user);
// ❌ 不安全:直接返回
return res.json(user); // 可能包含 password 字段
调试技巧
查看请求详情
const handler = async (req, res) => {
console.log('Request details:', {
method: req.method,
url: req.originalUrl,
headers: req.headers,
query: req.query,
body: req.body,
});
};
测试接口
使用 curl 测试:
# GET 请求
curl http://localhost:3052/api/project/heartbeat
# POST 请求
curl -X POST http://localhost:3052/api/project/getOneAccount \
-H "Content-Type: application/json" \
-d '{"EMPLOYEE_ID": "123456"}'
# 带认证的请求
curl http://localhost:3052/api/project/protected \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
常见问题
Q: 路由不生效?
检查以下项:
- 确保
routes.js在正确的位置:projects/{project}/api/routes.js - 文件必须导出默认数组:
export default [...] - 重启服务器以加载新路由
- 检查服务器日志是否有错误
Q: 如何访问系统内置功能?
您可以导入系统模块:
import DBConnector from '../../../src/db-connector.js';
import { signToken } from '../../../src/api/v1/auth.js';
import globalConfig from '../../../src/globalConfig.js';
Q: 能否使用外部 npm 包?
可以,首先安装依赖:
npm install axios
然后在代码中导入:
import axios from 'axios';
Q: 如何处理文件上传?
系统已集成 express-fileupload,直接使用:
const uploadHandler = async (req, res) => {
if (!req.files || !req.files.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
const uploadedFile = req.files.file;
const savePath = `/path/to/save/${uploadedFile.name}`;
await uploadedFile.mv(savePath);
return res.json({
success: true,
filename: uploadedFile.name
});
};
版本兼容性
- 支持 ES6+ 语法(ESM 模块)
- Node.js 版本要求:≥ 22.0
- 必须使用
import/export,不支持require