# groovy-web-debug-editor **Repository Path**: douzh/groovy-web-debug-editor ## Basic Information - **Project Name**: groovy-web-debug-editor - **Description**: groovy语言的在线编辑器 ✅ 动态 API 发布 - 无需重启即可发布新接口 ✅ 脚本调试 - 单步执行、变量查看、表达式评估 ✅ 断点调试 - 点击设置断点,逐步调试 ✅ 代码提示 - 支持 Java 类、方法的智能提示 ✅ API 管理 - 查看、编辑、删除已发布的 API ✅ 数据持久化 - 使用 SQLite + MyBatis 存储 API 配置 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 7 - **Forks**: 4 - **Created**: 2026-05-21 - **Last Updated**: 2026-06-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 动态 API 发布与调试平台 基于 **Spring Boot + Groovy + Monaco Editor** 的动态接口发布系统,支持脚本实时调试、断点设置、单步执行等功能。 **当前版本**: v2.0 ![](./assets/groovy-web-debug.webp) 在对编译类做缓存的情况下,性能和实际java编译后的性能差异不大。 ![](./assets/jmeter-test.png) ## 🚀 快速开始 ### 1. 克隆项目 ### 2. 编译项目 ```bash mvn clean package ``` ### 3. 运行应用 ```bash mvn spring-boot:run ``` ### 4. 访问界面 打开浏览器访问:http://localhost:8080 ## 📚 完整文档 详细的使用文档请查看:**[project-doc/README.md](./project-doc/README.md)** ## ✨ 核心功能 - ✅ 动态 API 发布 - 无需重启即可发布新接口 - ✅ 在线脚本编辑 - Monaco Editor 提供专业编辑体验 - ✅ 脚本调试 - 单步执行、变量查看、表达式评估(基于 Groovy AST) - ✅ 断点调试 - 点击设置断点,逐步调试 - ✅ 代码提示 - 支持 Java 类、方法的智能提示 - ✅ 引用项目类 - Groovy 脚本可调用 Java 类和 Spring Bean - ✅ API 管理 - 查看、编辑、删除已发布的 API - ✅ **方法管理** - 创建可复用的 Groovy 方法,支持跨接口调用 - ✅ 数据持久化 - 使用 SQLite + MyBatis 存储 API 和方法配置 ## 🌟 问题说明 - 断点调试不支持显示for循环的变量,如`i`、`j`等,可以将这些变量再次赋值查看或通过表达式监控功能查看。 - 断点调试不支持显示catch中的异常变量,如`e`,可以将在catch中将异常对象再次赋值查看或通过表达式监控功能查看。 - 调试时变量会显示为json化数据,会将非业务类型对象显示为`[Java Object: 类全路径]`,具体判断逻辑在`AstDebuggerService.isInternalClass()`方法中,查看这类变量可以使用表达式监控功能查看。 - 编辑器提示只能提示变量的一级属性和方法,子属性的不会提示,如`System.`会提示可用属性,`System.out.`不会提示out的属性。 - HTTP接口调用后端做的编译类缓存,如果多节点集群需要自己修改为redis缓存,或删除缓存逻辑。 ## 🛠️ 技术栈 | 分类 | 技术 | 版本 | |------|------|------| | 后端框架 | Spring Boot | 2.7.18 | | 脚本引擎 | Groovy | 3.0.19 | | 前端编辑器 | Monaco Editor | 0.44.0 | | 数据库 | SQLite | 3.42.0 | | ORM | MyBatis | 2.1.2 | | JSON 处理 | Gson | 2.10.1 | | Java 版本 | JDK | 11+ | ## 📝 快速示例 ### 发布第一个 API 1. 访问 http://localhost:8080 2. 填写 API 路径:`/hello` 3. 选择 HTTP 方法:`GET` 4. 编写脚本(Groovy/Java 语法): ```java return "Hello, World!"; ``` 5. 点击"保存" 6. 访问 http://localhost:8080/api/dynamic/hello 测试 ### 使用 Spring Bean ```java import java.util.List; import java.util.Map; import com.example.dynamicapi.mapper.DynamicApiMapper; // 获取 Spring Bean DynamicApiMapper mapper = SpringBeanUtils.getBean(DynamicApiMapper.class); // 查询数据 List> apis = mapper.selectAll(); return apis; ``` ### 调用其他接口(ApiCall) `ApiCall` 工具类允许脚本直接调用其他已注册的接口,无需 HTTP 请求。 #### 基本用法 ```java // 通过接口编码调用(推荐方式) Object result = ApiCall.call("USER_QUERY"); return result; ``` #### 带请求体调用 ```java import java.util.HashMap; import java.util.Map; // 准备请求体 Map body = new HashMap<>(); body.put("username", "张三"); body.put("age", 25); // 调用接口 Object result = ApiCall.call("USER_CREATE", body); return result; ``` #### 带请求头调用 ```java import java.util.HashMap; import java.util.Map; // 准备请求体和请求头 Map body = new HashMap<>(); body.put("userId", 123); Map headers = new HashMap<>(); headers.put("Authorization", "Bearer token"); // 调用接口 Object result = ApiCall.call("USER_INFO", body, headers); return result; ``` #### 检查接口是否存在 ```java if (ApiCall.exists("USER_QUERY")) { return ApiCall.call("USER_QUERY"); } return "接口不存在"; ``` #### ApiCall 方法列表 | 方法 | 说明 | 示例 | |------|------|------| | `ApiCall.call(code)` | 通过编码调用接口 | `ApiCall.call("USER_QUERY")` | | `ApiCall.call(code, body)` | 带请求体调用 | `ApiCall.call("USER_CREATE", body)` | | `ApiCall.call(code, body, headers)` | 带请求体和请求头 | `ApiCall.call("API", body, headers)` | | `ApiCall.exists(code)` | 检查接口是否存在 | `ApiCall.exists("USER_QUERY")` | | `ApiCall.getApiCount()` | 获取已注册接口数量 | `ApiCall.getApiCount()` | ### 调用方法(MethodCall) `MethodCall` 工具类允许脚本直接调用其他已注册的方法,实现代码复用。 #### 基本用法 ```java // 通过方法编码调用(无参数) Object result = MethodCall.call("HELLO_WORLD"); return result; ``` #### 带参数调用 ```java import java.util.HashMap; import java.util.Map; // 准备参数 Map params = new HashMap<>(); params.put("token", "abc123"); params.put("timeout", 3000); // 调用方法 boolean valid = (boolean) MethodCall.call("USER_VALIDATE", params); return valid; ``` #### 安全调用 ```java // 安全调用(捕获异常返回null) Object result = MethodCall.callSafe("SOME_METHOD", params); if (result == null) { return "方法调用失败"; } return result; ``` #### 便捷参数构建 ```java // 使用便捷方法构建参数 Object user = MethodCall.call("CREATE_USER", MethodCall.params("name", "张三", "age", 25, "active", true)); return user; ``` #### MethodCall 方法列表 | 方法 | 说明 | 示例 | |------|------|------| | `MethodCall.call(code)` | 通过编码调用方法 | `MethodCall.call("HELLO_WORLD")` | | `MethodCall.call(code, params)` | 带参数调用 | `MethodCall.call("USER_VALIDATE", params)` | | `MethodCall.callSafe(code)` | 安全调用(无参数) | `MethodCall.callSafe("SOME_METHOD")` | | `MethodCall.callSafe(code, params)` | 安全调用(带参数) | `MethodCall.callSafe("SOME_METHOD", params)` | | `MethodCall.callAs(code, clazz)` | 调用并转换类型 | `MethodCall.callAs("GET_USER", User.class)` | | `MethodCall.params(k1,v1,k2,v2...)` | 便捷参数构建 | `MethodCall.params("a",1,"b",2)` | ### 调试脚本 1. 编写脚本 2. 填写测试数据(如需要) 3. 点击"🐛 开始调试" 4. 使用单步、继续、停止控制调试 5. 查看变量和评估表达式 ### API 调用方式 #### 接口地址格式 ``` http://localhost:8080/api/dynamic/{path} ``` #### 传参说明 在 Groovy 脚本中,可以通过以下变量获取请求参数: | 变量名 | 类型 | 说明 | 示例 | |--------|------|------|------| | `body` | Map | 请求体(POST/PUT 请求的 JSON 数据) | `body.get("name")` | | `params` | Map | URL 查询参数 | `params.get("id")[0]` | | `headers` | Map | 请求头 | `headers.get("Authorization")` | #### 使用示例 **1. 获取 URL 查询参数** ```java // URL: /api/dynamic/hello?name=World String name = params.get("name") != null ? params.get("name")[0] : "Guest"; return "Hello, " + name + "!"; ``` **2. 获取请求体参数** ```java // POST 请求体: {"username": "张三", "age": 25} String username = body.get("username"); Integer age = ((Number) body.get("age")).intValue(); return "用户: " + username + ", 年龄: " + age; ``` **3. 获取请求头** ```java String token = headers.get("Authorization"); if (token != null) { return "Token: " + token.substring(0, 20) + "..."; } return "No token provided"; ``` **4. 完整示例:处理多种参数** ```java import java.util.HashMap; import java.util.Map; Map result = new HashMap(); // 获取路径参数(通过 path 变量) result.put("requestPath", path); // 获取 URL 参数 String queryName = params.containsKey("name") ? params.get("name")[0] : "未提供"; result.put("queryParam", queryName); // 获取请求体 if (body != null) { result.put("body", body); result.put("bodySize", body.size()); } // 获取请求头 result.put("contentType", headers.get("Content-Type")); result.put("userAgent", headers.get("User-Agent")); return result; ``` ## 📂 项目结构 ``` groovy-debug/ ├── project-doc/ # 项目文档 │ ├── 01-需求文档/ # 需求规格说明书 │ ├── 02-产品设计/ # 产品设计和交互设计 │ ├── 03-功能设计/ # 系统架构和功能设计 │ ├── 04-详细设计/ # 详细设计和实现细节 │ └── 05-测试/ # 测试用例和测试指南 ├── src/ │ └── main/ │ ├── java/ # Java 源代码 │ │ └── com/example/dynamicapi/ │ │ ├── config/ # 配置类 │ │ ├── controller/ # REST API 控制器 │ │ ├── debug/ # Groovy AST 调试模块 │ │ ├── entity/ # 数据库实体 │ │ ├── handler/ # API 请求处理器 │ │ ├── mapper/ # MyBatis Mapper │ │ ├── model/ # 数据模型 │ │ ├── service/ # 业务服务 │ │ └── util/ # 工具类 │ └── resources/ │ ├── mapper/ # MyBatis XML 映射文件 │ └── static/ # 前端静态资源 ├── test.db # SQLite 数据库文件 ├── pom.xml # Maven 配置 └── .gitignore # Git 忽略文件 ``` ## 📁 核心模块说明 | 模块 | 说明 | 关键文件 | |------|------|----------| | **config** | 应用配置 | DatabaseConfig, GroovyAstConfig, WebConfig | | **controller** | REST API | DynamicApiController, AstDebugController, JavaClassMetadataController | | **debug** | Groovy AST 调试 | DebugAstTransform, DebugCompilationCustomizer, DebuggerCallback | | **handler** | 请求处理 | DynamicApiHandler | | **service** | 业务逻辑 | DynamicApiService, AstDebuggerService, JavaClassMetadataService, ApiFileStorageService | | **mapper** | 数据访问 | DynamicApiMapper | | **entity** | 数据库实体 | DynamicApi | ## ⚠️ 注意事项 1. **安全性**:当前实现允许执行任意 Groovy 脚本,生产环境需添加安全限制 2. **持久化**:API 定义存储在 SQLite 数据库中,重启后数据不会丢失 3. **并发**:使用数据库事务保证数据一致性 ## 🔒 生产环境建议 - 添加身份认证和授权 - 限制可访问的 Java 类 - 设置脚本执行超时 - 实现脚本沙箱机制 - 添加 API 调用频率限制 ## 🤝 贡献指南 欢迎提交 Issue 和 Pull Request! ### 开发流程 1. Fork 本仓库 2. 创建特性分支 (`git checkout -b feature/your-feature`) 3. 提交更改 (`git commit -m 'Add some feature'`) 4. 推送到分支 (`git push origin feature/your-feature`) 5. 创建 Pull Request ### 代码规范 - 遵循 Java 代码规范(Google 风格) - 使用 4 空格缩进 - 添加必要的注释和文档 - 确保所有测试通过 ## 📄 许可证 本项目采用 **Apache License 2.0** 许可证。详见 [LICENSE](LICENSE) 文件。 --- **开始使用:** 查看详细文档 → [project-doc/README.md](./project-doc/README.md)