3. HTTP 协议与 RESTful API:HTTP 请求/响应模型、RESTful 设计原则、常见状态码

在契约压测与校验体系中,HTTP 协议是服务间通信的基石。理解 HTTP 请求/响应模型、RESTful 设计原则以及常见状态码,是编写精准契约、设计高效压测场景的前提。本章将系统梳理这些核心概念,并展示它们如何在 Go 契约测试中落地。

3.1 HTTP 请求/响应模型

HTTP 协议采用经典的客户端-服务器(Client-Server)模型。客户端发起请求(Request),服务器处理并返回响应(Response)。每一次完整的交互构成一个事务。

核心要点: 在契约测试中,我们通常将客户端(如 Go 的 http.Client)视为消费者,服务器(如 Gin、Echo 框架)视为提供者。契约即是对请求与响应结构的精确约定。

3.1.1 请求结构

一个标准的 HTTP 请求由以下部分组成:

  • 请求行(Request Line): 包含方法(GET/POST/PUT/DELETE 等)、URI 路径、HTTP 版本。
  • 请求头(Headers): 键值对集合,传递元数据(如 Content-Type、Authorization、Accept)。
  • 请求体(Body): 可选,通常用于 POST/PUT 请求,承载 JSON、XML 或表单数据。

3.1.2 响应结构

服务器返回的响应包含:

  • 状态行(Status Line): HTTP 版本、状态码、状态文本(如 200 OK)。
  • 响应头(Headers): 如 Content-Type、Content-Length、Cache-Control。
  • 响应体(Body): 返回的数据内容,通常为 JSON 格式。

3.1.3 请求-响应生命周期

下图展示了从客户端发起请求到接收响应的完整流程,以及契约测试的介入点:

HTTP 请求-响应生命周期与契约测试介入点 HTTP 请求-响应生命周期与契约测试介入点 客户端 (Consumer) 服务器 (Provider) ① 发送请求 (Method + URI + Headers + Body) ② 返回响应 (Status + Headers + Body) 契约测试介入点 验证请求/响应结构是否符合预定义契约
契约压测提示: 在压测场景中,每个请求的 Headers 和 Body 结构必须严格匹配契约定义。例如,若契约要求 Content-Type: application/json,压测脚本中必须显式设置该头,否则服务器可能返回 415 Unsupported Media Type。

3.2 RESTful 设计原则

REST(Representational State Transfer)是一种架构风格,而非协议。遵循 RESTful 原则的 API 更易于理解、扩展和契约化。以下是核心原则:

原则 说明 契约测试影响
资源导向 每个 URL 代表一个资源(如 /users/123),而非操作。 契约中的路径必须与资源模型一致,避免动词式路径。
无状态 每个请求包含所有必要信息,服务器不保存客户端上下文。 压测时每个请求需独立携带认证令牌等状态信息。
统一接口 使用标准 HTTP 方法:GET(查询)、POST(创建)、PUT(全量更新)、PATCH(部分更新)、DELETE(删除)。 契约必须明确每个端点的允许方法,压测时不可混用。
可缓存 响应应明确标记是否可缓存(Cache-Control 头)。 压测需考虑缓存策略对吞吐量的影响。
分层系统 客户端无需知道直接与哪一层通信(如网关、负载均衡)。 契约测试通常针对服务实例,但压测需考虑全链路。

3.2.1 RESTful 资源命名规范

良好的资源命名是契约清晰度的基础:

  • 使用名词复数形式:/users、/orders、/products。
  • 使用子资源表达层级关系:/users/{userId}/orders。
  • 使用查询参数过滤、排序、分页:/users?status=active&page=1&size=20。
  • 避免在 URL 中使用动词:/getUser 应改为 /users/{id}。

3.3 常见 HTTP 状态码

状态码是服务器对请求处理结果的数字表示。在契约测试中,状态码是断言的核心要素之一。以下是契约压测中最常遇到的分类:

分类 范围 常见状态码 含义 契约测试场景
1xx 信息 100-199 100 Continue 服务器已收到请求头,客户端可继续发送请求体。 较少直接断言,但压测中需处理 Expect 头。
2xx 成功 200-299 200 OK
201 Created
204 No Content
请求已成功处理。 契约中最常见的成功断言目标。
3xx 重定向 300-399 301 Moved Permanently
302 Found
304 Not Modified
需要客户端采取进一步操作完成请求。 压测需配置是否跟随重定向,契约应明确预期。
4xx 客户端错误 400-499 400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
422 Unprocessable Entity
请求包含错误语法或无法完成。 契约测试需覆盖边界条件,验证错误响应结构。
5xx 服务器错误 500-599 500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
服务器未能完成合法请求。 压测中应监控 5xx 比例,契约测试通常不模拟。
避坑指南: 在契约压测中,不要仅断言状态码为 200。例如,一个 POST 创建资源的端点,成功时应返回 201 Created 而非 200 OK。同时,对于 4xx 错误,契约应明确返回的 Body 结构(如错误码、消息字段),否则压测脚本无法准确判断失败原因。

3.4 RESTful API 契约结构示例

以下是一个典型的 RESTful 用户管理 API 的契约定义片段,展示了请求/响应模型、状态码与资源命名的结合:

// 契约:创建用户
// POST /api/v1/users
// 请求体
{
  "username": "string (required, 3-50 chars)",
  "email": "string (required, valid email format)",
  "role": "string (optional, default: 'user')"
}
// 成功响应 (201 Created)
{
  "id": "string (uuid)",
  "username": "string",
  "email": "string",
  "role": "string",
  "created_at": "string (ISO 8601)"
}
// 错误响应 (400 Bad Request)
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Username is required",
    "field": "username"
  }
}
// 错误响应 (409 Conflict)
{
  "error": {
    "code": "DUPLICATE_EMAIL",
    "message": "Email already exists"
  }
}

3.5 状态码在契约压测中的策略

在压测场景中,不同状态码的分布直接反映系统健康度。以下是一个典型的压测断言策略表:

状态码范围 压测预期比例 处理策略
2xx > 99% 正常业务流量,记录响应时间分布。
4xx < 1% 通常由非法请求导致,需检查压测脚本数据生成逻辑。
5xx 0% (理想) 立即告警,表明服务端存在 Bug 或资源耗尽。
3xx 视场景而定 若未配置跟随重定向,需检查是否触发了意外跳转。
实战建议: 在 Go 契约压测脚本中,使用 http.Client 的 CheckRedirect 字段控制重定向行为。对于 RESTful API,通常应禁止自动跟随重定向,以便契约测试能捕获到 3xx 响应并验证其 Location 头是否符合预期。

3.6 本章小结