# **2.2 API 接口设计规范** API 是前后端、微服务之间沟通的**核心契约**。优秀的 API 设计不仅能大幅降低沟通成本,还能提升系统的稳定性和可维护性。一旦 API 契约对外发布,就必须保持向前兼容。 ### **2.2.1 协议选型与设计原则** 根据不同的业务场景,我们支持以下三种主要的接口协议,严禁在不恰当的场景滥用协议: #### **1\. RESTful API (默认首选)** 适用于绝大多数 Web 端、App 端的外部接口以及常规业务交互。 * **面向资源:** URL 必须全部使用**名词**的复数形式,严禁在 URL 中包含动词(如 /get-users 是错误示范,应为 GET /users)。名词推荐使用短横线分隔(Kebab-case,如 /user-profiles)。 * **HTTP 动词语义化:** * GET:查询资源,幂等,绝对不能产生副作用(如修改数据)。 * POST:创建新资源,非幂等。 * PUT:全量更新资源,幂等。 * PATCH:局部更新资源。 * DELETE:删除资源。 * **版本控制:** API 必须具备版本号,推荐放在 URL 路径中(如 /api/v1/users),以便于后续重大重构时的平滑迁移。 * **务实原则:** 追求“实用级” REST(Level 2),不强求满足极致的 HATEOAS(Level 3,返回一堆超链接),避免过度设计增加前端解析负担。 #### **2\. gRPC (内部微服务首选)** 适用于**后端微服务之间**的高频、内部通信。 * **优势:** 基于 HTTP/2 和 Protobuf,强类型约束,序列化体积小,性能远超 HTTP+JSON。 * **规约:** 严禁将 gRPC 服务直接暴露给公网 Web/App 端(除非有特定的 API Gateway 或 gRPC-Web 转换层)。对外一律暴露 RESTful 或 GraphQL。 #### **3\. GraphQL (复杂聚合场景可选)** 适用于 BFF(聚合层)或前端对数据灵活性要求极高、且存在严重“过度获取(Over-fetching)”或“获取不足(Under-fetching)”的复杂场景。 * **规约:** 不要在简单的 CRUD 业务中引入 GraphQL,它会极大地增加后端的 N+1 查询优化难度和权限控制复杂度。必须在架构决策(ADR)中经过严格评审后方可使用。 ### **2.2.2 统一的请求头 (Headers) 与鉴权机制** 为了实现全链路追踪、多语言支持和统一的安全管控,所有 HTTP 请求必须遵循以下 Headers 规范: #### **1\. 标准请求头列表** * Authorization: 鉴权 Token(必须)。格式为 Bearer \。 * X-Request-Id (或 Trace-Id): 全链路追踪 ID。由网关或客户端生成,后端原样透传并在日志中打印,这是排查线上客诉的唯一生命线。 * X-Client-Type: 客户端类型(如 iOS, Android, Web, MiniProgram),用于后端进行渠道统计或特定逻辑下发。 * X-Client-Version: 客户端版本号(如 1.0.5),用于后端做兼容性控制。 * Accept-Language: 多语言标识(如 zh-CN, en-US),用于国际化错误提示。 #### **2\. 鉴权机制 (JWT)** * 系统默认采用 **JWT (JSON Web Token)** 进行无状态鉴权。 * **安全性底线:** * 严禁在 JWT 的 payload 中携带用户密码、敏感业务数据等隐私信息(JWT 仅防篡改,不防窃听,前端可直接 Decode)。 * Token 必须设置合理的过期时间(Access Token 建议 2 小时内,Refresh Token 建议 7 天)。 * 登出(Logout)或踢人操作,必须依赖 Redis 黑名单机制(Token 屏蔽),不能仅靠客户端删除 Token。 ### **2.2.3 标准响应数据格式 (Response 包装类)** *注:考虑到不同技术栈和框架的差异(如 Django DRF 原生倾向于直接返回资源 JSON 而不包外层,或者 GraphQL 有其自有的结构),本章节定义的 Response 包装格式为**推荐规范**。建议团队根据实际业务需求与框架特性灵活决策。一旦选定,同一项目/子系统内必须保持格式一致,避免前端解析困难。* #### **1\. 推荐标准外层结构(如需包装)** 如果业务决定采用统一包装格式,建议结构如下: { "code": 20000, // 业务状态码(非 HTTP 状态码) "message": "success", // 面向开发者的提示信息 / 面向用户的国际化错误提示 "data": { ... }, // 实际的业务数据(对象或数组) "traceId": "9b1deb4d..." // 必返:当前请求的追踪ID,方便前端直接带此ID报障 } #### **2\. 分页数据标准结构与实现方案** 分页的实现方式多种多样,不同的框架往往有其自带的最佳实践(如 Spring Data 的 Page 接口,Django DRF 的 PageNumberPagination 或 CursorPagination)。在此仅作**推荐与指导性建议**。 团队在设计分页接口时,应根据具体的业务端(Admin Web 或 App 瀑布流)选择合适的方法: **方案 A:传统页码分页 (Offset/Limit)** * **适用场景:** 后台管理系统 (Admin)、PC 端数据表格(允许用户指定跳到第 N 页)。 * **推荐返回结构示例:** { "data": { "list": \[ {...}, {...} \], // 当前页的数据列表 "total": 150, // 总条数 (注意:数据量极大时 count() 会有性能瓶颈) "page": 1, // 当前页码 "pageSize": 20, // 每页大小 "hasNext": true // 是否还有下一页 } } **方案 B:游标分页 (Cursor-based / Keyset Pagination)** * **适用场景:** 移动端 App 的“无限滚动”瀑布流、社交动态流。能有效解决数据实时增删导致的“数据重复出现”或“数据漏滑”问题,且性能极佳。 * **推荐返回结构示例:** { "data": { "list": \[ {...}, {...} \], // 当前页的数据列表 "next\_cursor": "eyJpZCI6IDE1MiwgImNyZWF0ZV90aW1lIjogIjIwMjM..." // 用于请求下一页的游标 (通常是 Base64 编码的最后一条数据标识) } } *规约提醒:不强制所有分页结构完全统一,但后端在输出分页数据时,应顺应其所用框架的原生分页器能力,并在 Swagger/API 文档中清晰标明采用的分页模式。* #### **3\. Null 值与空值规约 (极度容易引发 NPE 和前端白屏)** 不论外层是否包装,实际数据体中的空值处理应遵循: * **集合/数组:** 如果列表为空,必须返回空数组 \[\],**严禁返回 null**。 * **对象:** 如果对象为空,可以返回 {} 或 null(需与前端约定好)。 * **字符串:** 如果字符串为空,必须返回 "",**严禁返回字符串 "null"**。 * **布尔值:** 严禁使用 0/1 替代 true/false,JSON 必须保持类型严谨。 ### **2.2.4 全局错误码 (Error Codes) 分配与定义字典** *注:对于敏捷开发或复杂度一般的单体项目,维护庞大的五位数业务错误码字典往往会带来过高的管理成本。因此,本节为**推荐规范**。团队应根据项目规模灵活决策:轻量级项目完全可以仅依靠“标准 HTTP 状态码 \+ 清晰明确的 message”来处理异常;如果业务链路较长,需要精细化区分错误类型,则推荐引入以下错误码规范。* 我们需要明确区分 **HTTP 协议状态码** 与 **业务错误码**。 #### **1\. HTTP 状态码规约(代表网络与网关层面的状态)** 后端必须正确使用 HTTP 状态码,严禁将所有请求都返回 200 OK,即使系统内部发生了致命异常。 * 200 OK: 请求成功,业务逻辑被执行。 * 400 Bad Request: 参数校验失败(如缺少必填字段,格式错误)。 * 401 Unauthorized: 鉴权失败(Token 缺失、无效或过期)。前端收到后应统一跳转登录页。 * 403 Forbidden: 权限不足(Token 有效,但当前角色无权操作)。 * 404 Not Found: 路由不存在(资源未找到)。 * 429 Too Many Requests: 触发限流,请稍后重试。 * 500 Internal Server Error: 后端发生了未捕获的严重异常(代码 Bug 或数据库宕机)。 #### **2\. 业务错误码规约 (Response Body 中的 code 字段,若启用包装类)** 若团队决定启用全局错误码,推荐采用 **5 位数字** 或 **纯字符串枚举**(如 USER\_NOT\_FOUND)。以 5 位数字为例:A-BB-CC。 * **A (1位):系统级分类** * 2:成功。 * 4:客户端调用错误(业务约束拦截)。 * 5:服务端内部错误(业务预期内的异常)。 * **BB (2位):模块或子系统编号** * 00:全局公共错误。 * 01:用户中心。 * 02:订单中心。 * **CC (2位):具体错误序号** **全局核心字典示例:** * 20000: 业务执行成功。 * 40000: 客户端错误通配。 * 40102: 账户已被冻结。 * 40202: 账户余额不足。 * 50000: 服务器内部错误通配。 *规约:如果决定使用错误码,则禁止在代码中硬编码(如 return new Response(40102, "被冻结")),必须统一定义在 ErrorCodeEnum 枚举类中集中管理。* ## 🤖 [附加] AI 助手执行协议 (AI Output Schema) **绝对红线**:接口文档必须输出为 OpenAPI 3.0 YAML 格式;所有 HTTP 接口的响应体必须强制包裹在统一的 `{code, msg, data}` JSON 结构中。