本次重构实现了真正的预定义服务模式:服务定义包含具体的 URL/命令和参数映射配置,客户端只需要提供业务参数,系统负责将业务参数映射到实际的 API 调用或命令执行。
客户端传入执行细节(url、command、headers 等):
{
"serviceId": "web-api",
"payload": {
"url": "https://api.example.com/data", // ❌ 客户端传入 URL
"method": "GET", // ❌ 客户端传入方法
"headers": {...} // ❌ 客户端传入请求头
}
}服务定义包含执行细节,客户端只传业务参数:
服务定义(config/services.json):
{
"id": "weather-api",
"type": "WEB_SERVICE",
"description": "查询城市天气",
"parameters": {
"city": {
"type": "string",
"required": true,
"description": "城市名称"
}
},
"config": {
"url": "https://api.openweathermap.org/data/2.5/weather", // ✅ 服务定义中的 URL
"method": "GET", // ✅ 服务定义中的方法
"headers": { "Accept": "application/json" }, // ✅ 服务定义中的请求头
"paramMapping": {
"query": { "city": "q" } // ✅ 参数映射规则
}
}
}客户端请求:
{
"serviceId": "weather-api",
"payload": {
"city": "London" // ✅ 只传业务参数
}
}系统自动构建:
GET https://api.openweathermap.org/data/2.5/weather?q=London
文件: server/types/schemas.ts
新增了 WebServiceConfig 和 LocalToolConfig 两种配置类型:
WebServiceConfig:
{
url: string // 具体的 API 端点(可包含路径参数占位符)
method: 'GET' | 'POST' | ... // HTTP 方法
headers: Record<string, string> // 固定的请求头
timeout: number // 超时时间
paramMapping: {
query: Record<string, string> // 查询参数映射 {"clientParam": "apiParam"}
body: Record<string, string> // 请求体参数映射
path: Record<string, string> // 路径参数映射
}
}LocalToolConfig:
{
command: string // 具体的命令或脚本路径
args: string[] // 固定参数
cwd: string // 工作目录
paramMapping: {
args: Record<string, number> // 参数位置映射 {"clientParam": 0}
env: Record<string, string> // 环境变量映射 {"clientParam": "ENV_VAR"}
}
}系统支持三种参数映射方式:
-
查询参数映射 (
query){ "paramMapping": { "query": { "city": "q", // 客户端的 city 映射到 API 的 q 参数 "units": "units" } } }客户端传入
{"city": "London"}→ API 调用?q=London -
路径参数映射 (
path){ "url": "https://api.example.com/users/{userId}/posts", "paramMapping": { "path": { "userId": "userId" } } }客户端传入
{"userId": "123"}→ URL 变为/users/123/posts -
请求体参数映射 (
body){ "paramMapping": { "body": { "username": "user", "email": "email" } } }客户端传入
{"username": "john"}→ 请求体{"user": "john"}
-
参数位置映射 (
args){ "command": "backup-tool", "args": ["--format", "sql"], // 固定参数 "paramMapping": { "args": { "database": 2, // 客户端的 database 放在位置 2 "outputDir": 3 } } }最终命令:
backup-tool --format sql {database} {outputDir} -
环境变量映射 (
env){ "paramMapping": { "env": { "apiKey": "API_KEY", // 客户端的 apiKey 映射到环境变量 API_KEY "token": "AUTH_TOKEN" } } }
文件: config/services.json
现在包含完整的执行配置:
[
{
"id": "weather-api",
"type": "WEB_SERVICE",
"description": "查询城市天气信息",
"parameters": {
"city": {
"type": "string",
"required": true,
"description": "城市名称"
}
},
"config": {
"url": "https://api.openweathermap.org/data/2.5/weather",
"method": "GET",
"paramMapping": {
"query": { "city": "q" }
}
},
"output": "JSON"
}
]文件: server/services/TaskExecutor.ts
WebServiceExecutor 现在:
- 从服务配置中读取 URL、method、headers
- 根据 paramMapping 构建实际的请求
- 支持路径参数替换(
{param}占位符) - 支持查询参数和请求体参数映射
LocalToolExecutor 现在:
- 从服务配置中读取 command、args、cwd
- 根据 paramMapping 构建完整的命令行参数
- 支持环境变量映射
- 支持参数位置映射
- 只有在
config/services.json中定义的服务才能被执行 - 客户端无法注入任意服务定义
- 严格验证参数类型
- 拒绝未定义的参数
- 确保必需参数都已提供
- 命令白名单(只允许字母、数字、下划线、连字符、点和斜杠)
- 禁用 shell 执行(
shell: false) - 参数数组隔离(防止命令注入)
在 config/services.json 中添加新服务:
{
"id": "my-new-service",
"type": "WEB_SERVICE",
"description": "我的新服务",
"parameters": {
"endpoint": {
"type": "string",
"required": true,
"description": "服务端点"
},
"timeout": {
"type": "number",
"required": false,
"description": "超时时间(毫秒)"
}
},
"output": "JSON"
}curl http://localhost:3005/api/v1/servicescurl -X POST http://localhost:3005/api/v1/schedule \
-H "Content-Type: application/json" \
-d '{
"serviceId": "my-new-service",
"payload": {
"endpoint": "https://api.example.com/data"
},
"queue": "default"
}'- ✅ 服务必须预先定义 - 在配置文件中,不是运行时传入
- ✅ 参数严格验证 - 类型、必需性、是否允许
- ✅ 服务发现 API - 客户端可以查询可用服务
- ✅ 清晰的错误消息 - 指导用户正确使用
- ✅ 安全第一 - 多层防护,防止任意代码执行
| 方面 | 之前 | 现在 |
|---|---|---|
| 服务定义 | 可能暗示可以动态传入 | 明确必须预先定义 |
| 参数验证 | 基本的 Zod 验证 | 详细的类型和必需性验证 |
| 服务发现 | 无 | GET /api/v1/services |
| 参数结构 | 简单字符串 | 结构化对象 |
| 错误消息 | 简单 | 详细且有指导性 |
| 测试脚本 | 基本功能测试 | 包含参数验证和服务发现测试 |
本次重构完全实现了预定义服务模式,确保:
- 系统管理员完全控制可执行的服务
- 客户端只能引用预先批准的服务
- 严格的参数验证防止错误使用
- 清晰的 API 让客户端发现可用服务
- 多层安全措施保护系统安全
这种设计模式是构建安全、可控的任务调度系统的最佳实践。