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 请求-响应生命周期
下图展示了从客户端发起请求到接收响应的完整流程,以及契约测试的介入点:
契约压测提示: 在压测场景中,每个请求的 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 头是否符合预期。