页面请求上下文与后端调试
Spark Page 在页面设计、设计预览和应用运行时都会调用后端接口。为了让同一份页面配置能够连接开发、测试和生产环境,并让后端区分“设计器正在快速预览”与“用户正在使用应用”,平台会在页面请求中统一携带两个标准请求头。
这套协议适用于页面元数据、Custom URL、Model Data Source、文件上传、表格行操作,以及 Interaction 中的 Send Request 等页面请求。控件只负责配置业务 URL 和业务参数,不需要逐个手工填写环境请求头。
1. 整体流程
一次设计预览请求按以下顺序执行:
Page Designer 配置 Data Source / Interaction
│
▼
Spark 页面共享请求层解析当前环境与渲染模式
│
├── X-Spark-Page-Environment: DEV
└── X-Spark-Page-Render-Mode: DESIGN_PREVIEW
│
▼
后端 Controller 使用 SparkPageRequestHeaders 解析标准上下文
│
▼
Service 根据环境与渲染模式选择预览数据或对应环境的数据服务
│
▼
统一返回 ApiResult<DTO>,页面按配置的数据路径和字段映射渲染
设计器和运行时使用同一个共享请求层,因此在设计态测试通过的 URL、请求参数、Connection 和响应解析规则,会按同一契约用于页面预览和运行态。
2. 请求头协议
| 请求头 | 可用值 | 默认值 | 说明 |
|---|---|---|---|
X-Spark-Page-Environment | DEV、TEST、PROD | DEV | 当前页面连接的应用环境 |
X-Spark-Page-Render-Mode | DESIGN_PREVIEW、RUNTIME | RUNTIME | 当前请求来自设计预览还是应用运行态 |
两个维度彼此独立,常见组合如下:
| 使用位置 | Environment | Render Mode | 后端建议 |
|---|---|---|---|
| Page Designer 画布、Data Source 测试 | DEV | DESIGN_PREVIEW | 返回稳定、可重复、覆盖典型字段的开发预览数据;避免危险写操作 |
| 开发环境应用运行态 | DEV | RUNTIME | 连接开发数据库或开发服务,验证真实业务流程 |
| 测试环境应用运行态 | TEST | RUNTIME | 连接测试数据与测试依赖 |
| 生产环境应用运行态 | PROD | RUNTIME | 连接生产服务,并执行完整认证、授权和审计 |
如果在 Page Designer URL 中选择了其他环境,平台会使用当前选择的环境值;设计预览模式仍为 DESIGN_PREVIEW。
3. 前端不需要手工配置协议头
页面设计器、运行时 Shell 和共享请求层负责集中注入标准请求头。即使控件的 Headers 或 Connection 中存在同名配置,平台上下文值也应覆盖它们,避免页面作者把 DEV 页面误标成 PROD。
控件只需要配置:
- 业务 URL 与请求方式
- 业务查询参数或请求体
- Response List Path 等响应解析规则
- 必要的 Connection Key
不要在每个 Tree、ProTable 或 Send Request 中重复添加 X-Spark-Page-* 请求头,也不要在单个控件代码中硬编码 DEV 或 DESIGN_PREVIEW。
4. Java Controller 解析上下文
Java 17 / Spring Boot 4 后端使用 spark-base 提供的共享常量和模型。Controller 只解析一次,再把标准化的 SparkPageRequestContext 传入 Service。
@RestController
@RequestMapping("/spark/api/catalog/tree")
public class CatalogTreeController {
private final CatalogTreeService treeService;
public CatalogTreeController(CatalogTreeService treeService) {
this.treeService = treeService;
}
@GetMapping("/nodes")
public ApiResult<TreeDataResult> nodes(
@RequestParam(required = false) String parentId,
@RequestHeader(value = "Accept-Language", required = false)
String language,
@RequestHeader(value = SparkPageRequestHeaders.ENVIRONMENT,
required = false) String environment,
@RequestHeader(value = SparkPageRequestHeaders.RENDER_MODE,
required = false) String renderMode) {
Locale locale = SparkApiResultHelper.resolveLocale(language);
SparkPageRequestContext context =
SparkPageRequestHeaders.resolve(environment, renderMode);
TreeDataResult data = treeService.query(parentId, context);
return SparkApiResultHelper.success(
locale, data, "CATALOG-TREE-QUERY-SUCCESS");
}
}
业务 Service 不应接收原始 Header 字符串或临时 Map。统一模型可以避免不同接口各自解释 dev、preview 等非标准值。
5. Service 根据上下文选择数据
开发态分支的目的不是在前端伪造数据,而是让真实后端接口知道这次调用用于快速预览和调试。推荐把“选择哪一个数据提供者”集中在 Service 或专门的路由组件中,Controller 和 DTO 契约保持不变。
@Service
public class CatalogTreeService {
private final CatalogTreeRepository repository;
private final CatalogTreePreviewProvider previewProvider;
public CatalogTreeService(
CatalogTreeRepository repository,
CatalogTreePreviewProvider previewProvider) {
this.repository = repository;
this.previewProvider = previewProvider;
}
public TreeDataResult query(
String parentId,
SparkPageRequestContext context) {
List<TreeNodeDTO> nodes = isDevelopmentPreview(context)
? previewProvider.query(parentId)
: repository.query(parentId, context.getEnvironment());
return new TreeDataResult(nodes, context);
}
private boolean isDevelopmentPreview(SparkPageRequestContext context) {
return context.getEnvironment() == SparkPageEnvironment.DEV
&& context.getRenderMode()
== SparkPageRenderMode.DESIGN_PREVIEW;
}
}
预览数据提供者建议遵守以下原则:
- 返回稳定且可重复的数据,刷新画布或再次测试时结果不应随机变化。
- 覆盖真实 DTO 的关键字段、空值和典型层级,帮助页面作者检查映射。
- 默认只读;创建、删除、付款、发消息等有副作用的接口应提供明确的开发沙箱,或在设计预览模式下拒绝执行。
DEV + RUNTIME仍可连接开发数据库,用于验证真实流程,不要与DEV + DESIGN_PREVIEW混为一谈。- TEST、PROD 的数据路由由后端配置决定,不能相信前端请求头来绕过权限控制。
6. Data Source 测试
支持测试的数据源在配置弹窗中提供 Test。以 Tree 为例:
- 填写 Root Data URL、Request Method、Request Parameters 和 Response List Path。
- 点击 Test。
- Spark 使用当前页面环境和
DESIGN_PREVIEW发起真实请求。 - 面板显示最终 URL、业务参数、HTTP 状态、耗时和实际响应。
- Spark 使用当前 Response List Path 与字段映射解析响应,并显示识别出的节点数量。
- 根据结果调整
dataPath、Title Field、Value Field、Leaf Field 等配置,确认无误后再保存。
测试功能与设计画布、运行态复用同一个请求构造器。它不会额外保存数据源,也不会把认证、Cookie、Access Key 或 Connection 中的敏感 Header 显示在结果面板中;响应中常见的密码、Token、Secret 字段会被遮罩。
Tree 的完整配置过程参见 Tree 控件介绍。
7. 外部服务的 CORS 配置
如果 Custom URL 指向不同域名,外部服务除了允许常规方法和认证头,还必须在 CORS 中允许这两个请求头:
X-Spark-Page-Environment
X-Spark-Page-Render-Mode
Spring MVC 示例:
@Configuration
public class WebCorsConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://spark.hiforce.ai")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders(
"Content-Type",
"Authorization",
SparkPageRequestHeaders.ENVIRONMENT,
SparkPageRequestHeaders.RENDER_MODE);
}
}
生产项目应按实际 Spark 域名和安全策略收窄 Origin、方法和 Header 范围。
8. 安全边界
X-Spark-Page-Environment 和 X-Spark-Page-Render-Mode 是页面运行上下文,不是可信身份信息。浏览器请求头可以被修改,因此后端仍必须独立完成:
- 登录状态和 Access Token 校验
- 租户、用户和资源权限检查
- 写操作的业务校验与审计
- DEV、TEST、PROD 数据源的服务端隔离
- 敏感字段过滤和日志脱敏
不能因为请求声明为 DESIGN_PREVIEW 就跳过认证,也不能因为请求声明为 PROD 就直接连接任意生产租户数据。
9. 调试检查表
接口没有按预期返回时,按以下顺序检查:
- 在 Data Source 中点击 Test,确认最终 URL 和业务参数正确。
- 确认结果面板显示预期的 Environment 与 Render Mode。
- 在浏览器 Network 中检查两个标准请求头是否存在。
- 检查后端是否使用
SparkPageRequestHeaders.resolve(...),而不是自行比较大小写字符串。 - 检查 Service 的预览分支是否返回与正式 DTO 相同的 JSON 结构。
- 检查 Response List Path 和字段映射是否与实际响应一致。
- 跨域请求失败时检查 OPTIONS 响应与 Allowed Headers。
- 运行态正常但设计态失败时,检查后端是否错误地拒绝
DESIGN_PREVIEW,或预览数据是否缺少必需字段。