Skip to content

API 对接规范文档

适用对象:通过 /api/* 接入 WordForge 后端的 end-user 客户端(Web、iOS、Android、wordforge-web 等)。 本文档描述全局约定,包括认证机制、请求格式、响应结构、错误处理、限流规则等。

不在本文范围:内嵌的 admin GUI(/admin/* 路由)走独立的 /api/admin/* + Admin Token,属于后端的运维管理面,不属于"客户端"范畴。


1. 基础约定

项目说明
基础路径/api
数据格式JSON(Content-Type: application/json
字段命名camelCase(服务端统一使用 serde(rename_all = "camelCase")
时间格式ISO 8601 UTC,例:2024-01-15T08:30:00Z
ID 格式UUID v4 字符串
最大请求体2 MiB(超出返回 413)
CORSCORS_ORIGIN 环境变量配置,默认 http://localhost:5173

2. 认证机制

2.1 令牌体系

系统采用双令牌架构:

令牌类型有效期(默认)用途
Access TokenHS256 JWT24 小时访问所有受保护 API
Refresh TokenHS256 JWT7 天刷新 Access Token
Admin TokenHS256 JWT2 小时访问管理员 API

JWT Claims 结构:

json
{
  "sub": "<userId>",
  "token_type": "user" | "refresh" | "admin",
  "iat": 1705300200,
  "exp": 1705386600,
  "jti": "<uuid>"
}

2.2 Token 传递方式

客户端携带 Access Token 的两种方式(优先级从高到低):

  1. HTTP Header(推荐)

    Authorization: Bearer <accessToken>
  2. Cookie(备选)

    Cookie: token=<accessToken>

Refresh Token 传递方式:

  1. Authorization: Bearer <refreshToken>(刷新接口专用)
  2. Cookie: refresh_token=<refreshToken>

2.3 Token 获取与刷新流程

注册/登录 → 返回 accessToken + 写入 Cookie(token, refresh_token)

使用 accessToken 访问 API

accessToken 过期 → POST /api/auth/refresh(携带 refresh_token)

获得新的 accessToken(refresh_token 同时轮换,一次性使用)

关键安全机制:

  • Refresh Token 一次性使用:每次刷新后旧 token 立即失效,防止重放攻击
  • Token 以 SHA-256 哈希形式存储在数据库,原始 token 不落库
  • 登录失败 5 次后账号锁定 15 分钟
  • 修改密码/登出后,该用户所有 session 立即失效

3. 请求头规范

3.1 必须携带的请求头

Header适用场景说明
Content-Type: application/json所有 POST/PUT/PATCH 请求请求体为 JSON 时必须
Authorization: Bearer <token>所有受保护端点或通过 Cookie 传递

3.2 可选但推荐的请求头

Header类型说明
x-device-idstring设备唯一标识(UUID),用于设备追踪和封禁管理
x-device-platformstring设备平台,如 web / ios / android/api/telemetry 端点为必填(缺失或为 unknown400 MISSING_OS),其余端点缺省按 unknown 处理

注意: 携带 x-device-id 的请求,若对应设备被管理员封禁,将直接返回 403。

遥测端点的硬识别要求(自 v1.1.2-beta.4 / m038 起): /api/telemetry 对四要素做上线即生效、不受 strict-mode 开关控制的必填校验——平台 / 版本走 header(x-device-platformx-app-version),时区 / 型号走 payload(device.timezonedevice.model)。详见 §11 遥测载体契约。

3.3 服务端响应头

服务端会在响应中自动追加以下限流信息头:

Header说明
ratelimit-limit当前窗口最大请求数
ratelimit-remaining当前窗口剩余可用次数
ratelimit-reset窗口重置时间(Unix 时间戳)
retry-after当前限流窗口总秒数(仅 429 响应)

4. 统一响应结构

4.1 成功响应

json
{
  "success": true,
  "data": <T>
}

/health* 健康检查接口不使用统一 success/data 包装:/health/health/database/health/metrics 直接返回各自的健康数据;/health/live/health/ready 可能只返回 HTTP 状态码。

4.2 分页响应

json
{
  "success": true,
  "data": {
    "data": [ ... ],
    "total": 150,
    "page": 1,
    "perPage": 20,
    "totalPages": 8
  }
}

4.3 错误响应

json
{
  "success": false,
  "code": "AUTH_UNAUTHORIZED",
  "message": "令牌无效或已过期",
  "traceId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

当前中间件返回的 CLIENT_BANNEDMAINTENANCE 错误体不包含 success: false,结构为 {"code":"...","message":"...","traceId":"..."}。客户端应优先按 HTTP 状态码和 code 判断错误类型。

5xx 错误的 message 统一返回 "服务器内部错误",具体原因仅记录在服务端日志中。


5. 错误码规范

5.1 HTTP 状态码与错误码对照

HTTP 状态错误码触发场景
400INVALID_REQUEST_BODYJSON 解析失败或 Content-Type 缺失
400VALIDATION_ERROR字段校验失败(来自 Store 层)
400MISSING_OS/api/telemetry 缺少 x-device-platform 头或其值为 unknown(自 m038 起硬校验)
400MISSING_APP_VERSION/api/telemetry 缺少 x-app-version 头(自 m038 起硬校验)
400MISSING_TIMEZONE/api/telemetry payload 缺少 device.timezone(自 m038 起硬校验)
400MISSING_DEVICE_MODEL/api/telemetry payload 缺少 device.model(自 m038 起新增必填)
403DEVICE_NOT_REGISTERED/api/telemetry 设备未注册(需先正常登录使用后再上报)
403DEVICE_OWNERSHIP_MISMATCH/api/telemetry 设备归属与当前账号不符
400业务错误码各模块自定义前缀码,如 AUTH_* / LEARNING_* / WORDS_* / WORDBOOK_* / BATCH_TOO_LARGE
401AUTH_UNAUTHORIZED未携带 token 或 token 失效
403FORBIDDEN无权限、账号被封禁、注册关闭或用户数量达到上限
403CLIENT_BANNED设备被封禁(由中间件拦截)
404NOT_FOUND资源不存在
409CONFLICT资源冲突(如邮箱已注册)
413PAYLOAD_TOO_LARGE请求体超过 2 MiB
429RATE_LIMITED触发通用限流或账号因登录失败锁定
429AUTH_RATE_LIMITED触发认证端点限流
500INTERNAL_ERROR服务端内部错误(message 统一脱敏为 服务器内部错误
503MAINTENANCE服务器维护中(响应体结构见 §9,客户端应按 HTTP 503 或 code=MAINTENANCE 识别)

注意:账号被管理员封禁返回 403 FORBIDDEN;账号因连续登录失败 5 次被临时锁定则返回 429 RATE_LIMITED,两者不同。

5.2 认证错误消息参考

消息含义
"缺少认证令牌"未携带 Authorization 头或 token cookie
"令牌无效或已过期"JWT 签名错误或已超期
"令牌类型无效"使用了错误类型的 token(如用 refresh token 访问 API)
"会话不存在或已过期"Token 已被撤销或数据库中无对应记录
"用户已被封禁"账号被管理员封禁
"认证尝试次数过多,请稍后再试"认证接口触发限流

6. 限流规则

6.1 通用限流(所有 /api/* 端点)

参数默认值说明
时间窗口900 秒(15 分钟)固定窗口(窗口到期后计数归零)
最大请求数500 次每 IP 每窗口
超出后 HTTP 状态429
超出后错误码RATE_LIMITED

6.2 认证端点限流(更严格)

适用端点:/api/auth/register/api/auth/login/api/auth/refresh/api/auth/forgot-password/api/auth/reset-password/api/auth/verify-reset-token

参数默认值
时间窗口60 秒
最大请求数10 次
超出后错误码AUTH_RATE_LIMITED

6.3 IP 识别规则

  • 服务器配置 TRUST_PROXY=true 时,优先读取 x-forwarded-for(取第一个 IP),其次读取 x-real-ip
  • IPv6 地址聚合至 /64 前缀参与限流计数

7. 分页约定

所有列表接口统一使用如下查询参数:

参数类型默认值说明
pagenumber1页码,从 1 开始
perPagenumber20(/api/v1 记录类为 50)每页条数,最大 100

分页响应结构见第 4.2 节。响应中的 page 为请求传入的页码,可能大于 totalPages


8. 批量操作约定

  • 批量接口(/batch/batch-get 等)的数组长度上限由服务器 max_batch_size 配置决定(默认 500)
  • 批量操作部分失败时:HTTP 200,响应体包含 errors 数组指出失败条目
  • 批量操作全部成功时:创建类返回 201,查询/更新类返回 200

9. 维护模式

服务器维护期间,除以下端点外均返回 503 MAINTENANCE

  • /api/admin/*
  • /api/status
  • /api/realtime/*
  • /api/telemetry

响应体:

json
{
  "code": "MAINTENANCE",
  "message": "服务器维护中,请稍后重试",
  "traceId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

维护响应不包含 success 字段traceId 由请求 ID 中间件自动注入。客户端判断时应优先识别 HTTP 状态码 503 或 code 字段。


10. 数据类型约定

类型格式
IDUUID v4 字符串,如 "f47ac10b-58cc-4372-a567-0e02b2c3d479"
时间戳ISO 8601 UTC,如 "2024-01-15T08:30:00Z"
浮点数IEEE 754 f64
概率/比率0.0 ~ 1.0 的 f64
单词学习状态枚举"new" / "learning" / "reviewing" / "mastered" / "forgotten"(lowercase;v0.6.0-beta.4 P3#7 起;WordLearningState.state 字段)
注意:WordMasteryDecision.masteryLevel不同枚举,仍为 "NEW"/"LEARNING" 等 SCREAMING_SNAKE_CASE
会话状态枚举"active" / "completed" / "abandoned"
单词本类型枚举"system" / "user"(字段名为 type
学习记录类型枚举"learning" / "review" / "all"(字段名为 recordType,写入端可选;缺省落库 "all"
统计分类查询?category=learning|review|all,缺省或 all 等同不过滤;用于 /api/records/statistics/api/records/statistics/enhanced/api/word-states/stats/overview

11. 遥测载体契约(POST /api/telemetry

v1.1.2-beta.4(迁移 m038) 起,遥测端点对设备四要素做上线即生效、不受 strict_mode 开关控制的硬校验,缺任一即返回 4xx。客户端发版前务必补齐以下载体,否则上报全部被拦。

11.1 请求头(必填)

Header校验缺失/非法时
x-device-platform非空且 ≠ unknownweb / ios / android400 MISSING_OS
x-app-version非空(客户端版本号)400 MISSING_APP_VERSION
x-device-id设备 UUID(用于归属核验)归属态判定见 §11.3

11.2 请求体 payload.device(必填字段)

字段类型校验缺失/为空时
device.timezonestring非空(IANA 时区,如 Asia/Shanghai400 MISSING_TIMEZONE
device.modelstring非空(设备型号;Web 端无真型号可落 browser on OS 派生标识或 web-admin 占位)400 MISSING_DEVICE_MODEL
device.languagestringsession_start 时受 strict-mode 软门控strict 开启时 MISSING_LANGUAGE
device.screenWidth/screenHeight/pixelRatio/cpuCoresnumbersession_start 时受 strict-mode 软门控(均 > 0)strict 开启时 MISSING_DEVICE_FINGERPRINT

device.model 为 m038 新增必填字段。device.timezone 已由四要素硬校验接管,不再仅受 strict-mode 软门控(旧文档表述已过期)。

11.3 设备归属三态核验

四要素校验通过后,按 x-device-id 对应的设备 owner 做三态判定:

owner 态处理
设备未注册(无记录)403 DEVICE_NOT_REGISTERED
已注册但归属为其他账号403 DEVICE_OWNERSHIP_MISMATCH
已注册且归属当前账号放行
已注册但未认领(owner 为 NULL)claim 放行(不误伤老匿名设备)

11.4 心跳与遥测的协议依赖

POST /api/telemetry 除写入历史数据外,还充当设备心跳:每条通过归属核验的上报都会刷新该设备的心跳时间戳(不按 eventType 区分,且不受采样 / 限流影响——被采样丢弃的周期上报仍刷新心跳)。服务端 watchdog 每 5 秒扫描一次,对有活跃 SSE 连接的设备做心跳判定:

  • 距上次心跳 > 10 秒计 1 次 miss;
  • 连续 5 次 miss 即向该设备推送 SSE data_corrupted 事件(客户端应据此走重拉数据流程),随后 miss 计数归零;
  • 无活跃 SSE 连接的纯遥测设备不参与该判定,其心跳记录在 300 秒无新上报后被回收。

由此对客户端构成硬性约束:有活跃 SSE 连接期间,periodic 遥测必须以 ≤ 10 秒周期上报(建议 5–8 秒留网络抖动余量),否则累计 miss 会触发 data_corrupted。完整的心跳节奏、各 eventType 触发时机与 telemetry_request 即时上报约定见《客户端上传数据格式说明》§12.2。

WordForge — 智能英语学习平台