4. RESTful API 设计原则:资源命名、HTTP 方法语义、状态码使用、版本控制策略

RESTful API 是微服务与前后端分离架构的事实标准。在 Go 与 NestJS 混合架构中,契约压测的起点就是一套稳定、可预测的 API 设计。本章从资源命名、HTTP 方法语义、状态码使用、版本控制四个维度,建立可被压测工具直接验证的 API 契约规范。

4.1 资源命名:名词复数 + 层级路径

REST 的核心是“资源”(Resource),而非“动作”。资源命名应遵循以下规则:

  • 使用名词复数:/users、/orders、/products,避免动词如 /getUsers。
  • 层级关系用斜杠表达:/users/{id}/orders 表示用户下的订单集合。
  • 查询参数用于过滤/排序/分页:/orders?status=paid&page=1&size=20。
  • 禁止在路径中使用动词:/users/activate 应改为 PATCH /users/{id}/status。
✅ 正确示例:GET /api/v1/users/42/orders?status=completed
❌ 错误示例:GET /api/v1/getUserOrders?userId=42

4.2 HTTP 方法语义:CRUD 映射

每个 HTTP 方法对应一种标准操作语义,压测脚本直接依赖此语义构造请求。下表列出核心方法及其在契约中的行为约束:

HTTP 方法 语义 幂等 请求体 响应体
GET 查询资源(列表/详情) 是 无 资源表示
POST 创建资源 否 创建参数 新资源(201)
PUT 全量替换资源 是 完整资源 更新后资源
PATCH 部分更新资源 否 差异字段 更新后资源
DELETE 删除资源 是 无 204 无内容
压测提示:POST 和 PATCH 非幂等,在压测中需注意重复请求可能产生重复资源或状态冲突。建议在压测脚本中为 POST 使用唯一标识(如 UUID)作为请求体字段。

4.3 状态码使用:精确表达结果

状态码是契约压测断言的核心依据。错误的状态码选择会导致压测误判。以下是在 Go/NestJS 服务中必须遵守的状态码规范:

  • 2xx 成功:
    • 200 OK:GET、PUT、PATCH 成功返回资源。
    • 201 Created:POST 创建成功,响应头应包含 Location。
    • 204 No Content:DELETE 成功,或更新操作无需返回体。
  • 4xx 客户端错误:
    • 400 Bad Request:请求体校验失败、参数格式错误。
    • 401 Unauthorized:未提供或无效认证凭证。
    • 403 Forbidden:已认证但无权限。
    • 404 Not Found:资源不存在。
    • 409 Conflict:资源状态冲突(如重复创建、版本冲突)。
    • 422 Unprocessable Entity:业务逻辑校验失败。
  • 5xx 服务端错误:
    • 500 Internal Server Error:未捕获异常,压测中应视为严重缺陷。
    • 503 Service Unavailable:限流或熔断触发。
常见陷阱:不要将业务错误(如“余额不足”)返回 200 并在响应体中放 code: -1。这会破坏 HTTP 语义,导致压测工具无法通过状态码快速区分成功与失败。正确做法是返回 422 或 409。

4.4 版本控制策略:路径前缀 vs 请求头

API 版本控制是契约演进的基础。在 Go 与 NestJS 混合部署中,推荐以下两种策略:

策略 示例 优点 缺点
URL 路径前缀 /api/v1/users、/api/v2/users 直观、易于缓存、压测脚本可读性强 版本扩散后路径冗余
请求头(Accept-Version) Accept-Version: 2.0 路径干净、便于网关统一路由 调试不便、压测脚本需额外设置头
推荐:在契约压测闭环中优先使用 URL 路径前缀。压测工具(如 k6、wrk)对路径的解析和断言更直接,无需额外处理请求头逻辑。NestJS 中可通过 @Controller('api/v1/users') 实现;Go 中可通过路由分组 r.Group("/api/v1") 实现。
RESTful API 契约设计全景 RESTful API 契约设计全景 资源命名 /users/{id}/orders HTTP 方法语义 GET/POST/PUT/PATCH/DELETE 状态码使用 200/201/204/400/404/409 版本控制 /api/v1/ API 契约规范 压测断言依据 OpenAPI 文档 客户端 SDK 生成 💡 四个设计维度共同构成可压测、可文档化、可演进的 API 契约基础

4.5 契约示例:Go 与 NestJS 对照

以下展示一个用户资源在 Go(Gin)和 NestJS 中遵循相同契约的实现片段:

// Go (Gin) — 用户资源
r.GET("/api/v1/users/:id", handler.GetUser)      // 200 | 404
r.POST("/api/v1/users", handler.CreateUser)       // 201 | 400 | 409
r.PUT("/api/v1/users/:id", handler.UpdateUser)    // 200 | 400 | 404
r.DELETE("/api/v1/users/:id", handler.DeleteUser) // 204 | 404
// NestJS (TypeScript) — 用户资源
@Controller('api/v1/users')
export class UsersController {
  @Get(':id')           // 200 | 404
  findOne(@Param('id') id: string) {}

  @Post()               // 201 | 400 | 409
  create(@Body() dto: CreateUserDto) {}

  @Put(':id')           // 200 | 400 | 404
  update(@Param('id') id: string, @Body() dto: UpdateUserDto) {}

  @Delete(':id')        // 204 | 404
  remove(@Param('id') id: string) {}
}
契约一致性:在混合架构中,Go 微服务和 NestJS 网关应共享同一份 OpenAPI 规范文件。压测脚本基于该规范自动生成请求模板和断言规则,确保两端行为一致。

4.6 版本演进策略

当 API 需要破坏性变更时,遵循以下步骤保证压测闭环不中断:

  1. 新版本路径:创建 /api/v2/users,旧版本 /api/v1/users 继续运行。
  2. 双版本压测:压测脚本同时覆盖 v1 和 v2 的关键路径,对比响应时间与错误率。
  3. 弃用通知:在 v1 响应头中添加 Deprecation: true 和 Sunset: Sat, 31 Dec 2025 23:59:59 GMT。
  4. 下线验证:压测确认 v1 流量归零后,移除路由并更新契约文档。
压测风险:版本过渡期间,压测脚本必须明确指定 API 版本,避免因默认版本变更导致误测。建议在压测配置中将 base_url 与版本号拼接为完整路径。