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") 实现。
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 需要破坏性变更时,遵循以下步骤保证压测闭环不中断:
- 新版本路径:创建
/api/v2/users,旧版本/api/v1/users继续运行。 - 双版本压测:压测脚本同时覆盖 v1 和 v2 的关键路径,对比响应时间与错误率。
- 弃用通知:在 v1 响应头中添加
Deprecation: true和Sunset: Sat, 31 Dec 2025 23:59:59 GMT。 - 下线验证:压测确认 v1 流量归零后,移除路由并更新契约文档。
压测风险:版本过渡期间,压测脚本必须明确指定 API 版本,避免因默认版本变更导致误测。建议在压测配置中将
base_url 与版本号拼接为完整路径。