3. 接口契约总览

报表导出服务作为企业级数据平台的核心能力之一,其接口契约的规范性直接决定了上下游系统的集成效率与运维成本。本章从架构视角出发,围绕 RESTful 设计原则、资源路径规划与 HTTP 方法语义三个维度,构建一套可落地、可扩展的导出服务接口契约。

3.1 RESTful 设计原则在导出场景中的落地

报表导出并非简单的文件下载,而是一个包含提交 → 处理 → 查询 → 下载的异步生命周期。RESTful 原则在此场景下的核心映射如下:

REST 原则 导出场景映射 关键约束
资源导向 将每次导出请求视为一个 Export 资源 每个导出任务拥有唯一标识 exportId
无状态交互 服务端不保存客户端会话状态 所有请求携带认证令牌,状态通过资源属性表达
统一接口 使用标准 HTTP 方法操作导出资源 POST 创建、GET 查询、DELETE 取消
可缓存性 已完成导出的元数据可缓存 响应头显式标注 Cache-Control
分层系统 客户端不直接感知后端处理引擎 通过网关统一暴露,隐藏内部队列与计算节点
架构提示:导出服务天然具有“命令查询职责分离(CQRS)”的特征——提交导出是命令,查询状态与下载文件是查询。RESTful 设计恰好通过不同的 HTTP 方法天然支持这种分离,无需额外引入消息层。

3.2 资源路径规划:/api/v1/exports 层级设计

资源路径是接口契约的骨架。我们采用 集合资源 + 子资源 的层级结构,确保路径语义清晰、版本可控。

# 基础路径结构
/api/v1/exports                    # 导出任务集合
/api/v1/exports/{exportId}         # 单个导出任务
/api/v1/exports/{exportId}/file    # 导出文件下载端点
/api/v1/exports/{exportId}/logs    # 导出执行日志

路径规划遵循以下设计规则:

  • 版本前缀:/api/v1/ 作为稳定版本标识,避免破坏性变更影响存量客户端。
  • 名词复数:使用 exports 而非 export,符合 RESTful 集合资源惯例。
  • 子资源明确:file 和 logs 作为导出资源的附属子资源,通过路径层级表达从属关系。
  • 无动词路径:避免 /createExport、/downloadFile 等动词式路径,动作由 HTTP 方法表达。
避坑指南:不要将文件格式或参数编码到路径中(如 /api/v1/exports/csv),应通过请求体或查询参数传递。路径只表达资源标识,不表达资源形态。
导出服务资源路径层级与端点映射 /api/v1/exports POST → 创建导出任务 GET → 查询任务列表 /api/v1/exports/{exportId} GET → 查询任务详情 DELETE → 取消导出任务 /file GET → 下载导出文件 支持断点续传 /logs GET → 查询执行日志 分页返回 /metadata GET → 导出元数据 列定义、行数等

3.3 HTTP 方法语义与状态同步

每个 HTTP 方法对应导出任务生命周期中的一个特定操作。方法语义必须与资源状态严格对齐,避免语义混淆。

HTTP 方法 目标路径 语义说明 状态转换 幂等性
POST /api/v1/exports 提交新的导出请求,服务端创建任务并返回 exportId 无 → PENDING 否(每次创建新资源)
GET /api/v1/exports/{exportId} 查询单个导出任务的当前状态与元数据 不改变状态 是
GET /api/v1/exports 查询当前用户/租户的导出任务列表,支持分页与过滤 不改变状态 是
GET /api/v1/exports/{exportId}/file 下载已完成的导出文件,支持 Range 头实现断点续传 不改变状态 是
DELETE /api/v1/exports/{exportId} 取消正在处理或待处理的导出任务,清理资源 PENDING/PROCESSING → CANCELLED 是
设计警告:不要使用 PATCH 或 PUT 来“更新”导出任务的状态。导出任务的状态由服务端内部驱动,客户端不应直接修改。如果业务需要“重新导出”,应通过 POST 创建新的导出任务,而非复用已有资源。
HTTP 方法驱动的导出任务状态机 无状态 PENDING PROCESSING COMPLETED FAILED CANCELLED POST 内部调度 成功 异常 DELETE GET 查询不改变状态,可在任意状态节点调用 DELETE 仅对 PENDING 和 PROCESSING 状态生效,COMPLETED 和 FAILED 状态不可取消

3.4 请求与响应契约规范

接口契约的最终落地体现在具体的请求/响应结构上。以下以创建导出任务为例,展示完整的契约定义。

3.4.1 创建导出请求(POST /api/v1/exports)

// 请求头
Content-Type: application/json
Authorization: Bearer <token>
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000

// 请求体
{
  "reportType": "sales_summary",
  "format": "xlsx",
  "params": {
    "startDate": "2025-01-01",
    "endDate": "2025-03-31",
    "region": "华东",
    "includeCharts": true
  },
  "notify": {
    "email": "user@company.com",
    "webhook": "https://callback.company.com/export-complete"
  }
}

3.4.2 创建导出响应(201 Created)

// 响应头
Location: /api/v1/exports/a1b2c3d4-e5f6-7890-abcd-ef1234567890
Content-Type: application/json

// 响应体
{
  "exportId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "PENDING",
  "createdAt": "2025-04-01T10:30:00Z",
  "estimatedCompletion": "2025-04-01T10:32:00Z",
  "_links": {
    "self": { "href": "/api/v1/exports/a1b2c3d4-e5f6-7890-abcd-ef1234567890" },
    "status": { "href": "/api/v1/exports/a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
  }
}
HATEOAS 实践:响应中包含 _links 字段,引导客户端发现后续可执行的操作。例如,当任务状态变为 COMPLETED 时,_links 中会动态添加 download 链接,指向 /api/v1/exports/{exportId}/file。

3.5 接口契约检查清单

在交付接口契约前,请逐项核对以下清单:

  • 路径一致性:所有导出相关端点是否统一以 /api/v1/exports 为前缀?
  • 方法语义正确:是否严格遵循 POST 创建、GET 查询、DELETE 取消的语义映射?
  • 状态机完整:是否覆盖了 PENDING → PROCESSING → COMPLETED/FAILED/CANCELLED 所有路径?
  • 错误响应规范:是否统一使用 Problem JSON(RFC 7807)格式返回错误?
  • 幂等性保障:GET 和 DELETE 是否保证幂等?POST 是否在文档中明确标注非幂等?
  • 版本策略:是否通过 URL 前缀(/v1/)而非请求头管理版本?