3. HTTP 协议基础:HTTP 请求与响应、RESTful API 设计原则、常见状态码
在 Go 语言中实现契约校验,无论是作为服务端验证入参,还是作为客户端校验响应,都离不开对 HTTP 协议的深刻理解。本章将系统梳理 HTTP 请求与响应的结构、RESTful API 的设计原则以及常见状态码的语义,为后续的校验逻辑落地打下坚实基础。
3.1 HTTP 请求与响应结构
HTTP 协议采用经典的客户端-服务器模型。一次完整的交互包含一个请求(Request)和一个响应(Response)。
3.1.1 请求结构
一个标准的 HTTP 请求由以下四部分组成:
- 请求行(Request Line):包含方法(GET/POST/PUT/DELETE 等)、请求 URI 和协议版本(如 HTTP/1.1)。
- 请求头(Headers):键值对形式,携带元数据,如
Content-Type、Authorization、Accept等。 - 空行(Blank Line):分隔头部与消息体。
- 消息体(Body):可选,通常用于 POST、PUT 等方法传输数据。
Go 语言中的请求解析: 使用
net/http 标准库时,http.Request 结构体直接映射了上述结构。契约校验通常关注 r.Method、r.URL、r.Header 和 r.Body。
3.1.2 响应结构
响应结构与请求类似,但请求行替换为状态行:
- 状态行(Status Line):包含协议版本、状态码(如 200)和原因短语(如 OK)。
- 响应头(Headers):如
Content-Type、Content-Length、Set-Cookie等。 - 空行:分隔头部与消息体。
- 消息体(Body):服务器返回的数据,通常是 JSON、XML 或 HTML。
校验切入点: 在 Go 服务端,响应状态码和响应头是契约校验的第一道防线。例如,校验
Content-Type: application/json 是否与 API 文档一致。
3.2 RESTful API 设计原则
REST(Representational State Transfer)是目前最主流的 API 设计风格。契约校验的规则定义,通常直接来源于 RESTful 设计规范。
3.2.1 核心原则
| 原则 | 说明 | 对契约校验的影响 |
|---|---|---|
| 资源导向 | 每个 URL 代表一个资源(如 /users、/orders/123) |
校验 URL 路径参数是否符合资源标识符格式 |
| 使用标准 HTTP 方法 | GET 查询、POST 创建、PUT 全量更新、PATCH 部分更新、DELETE 删除 | 校验请求方法是否与预期操作匹配 |
| 无状态(Stateless) | 每个请求包含所有必要信息,服务端不保存客户端上下文 | 校验请求头中是否携带必要的认证令牌(Token) |
| 统一接口 | 使用一致的资源命名、响应格式和错误结构 | 校验响应体结构是否符合统一的 JSON Schema |
| 超媒体驱动(HATEOAS) | 响应中包含可操作的链接,引导客户端下一步操作 | 校验返回的链接 URL 是否合法且可达 |
3.2.2 资源命名规范
- 使用名词复数形式:
/users、/products,避免使用动词(如/getUser)。 - 层级关系用斜杠表示:
/users/123/orders表示用户 123 的订单列表。 - 查询参数用于过滤和排序:
/users?status=active&page=1,不用于资源标识。 - 版本号放在 URL 前缀或请求头:
/v1/users或Accept: application/vnd.api.v1+json。
常见反模式: 使用
GET /deleteUser?id=1 来删除资源。正确的做法是 DELETE /users/1。契约校验应能识别并拒绝此类不符合 RESTful 规范的请求。
3.3 常见 HTTP 状态码
状态码是服务端对请求处理结果的直接反馈。在契约校验中,状态码是判断 API 是否按约定工作的首要指标。
3.3.1 状态码分类
| 分类 | 范围 | 含义 | 校验场景 |
|---|---|---|---|
| 1xx 信息性 | 100 - 199 | 请求已接收,继续处理 | 较少直接校验,通常由底层库处理 |
| 2xx 成功 | 200 - 299 | 请求成功接收并处理 | 校验响应体是否包含预期数据 |
| 3xx 重定向 | 300 - 399 | 需要客户端进一步操作 | 校验 Location 头是否存在且合法 |
| 4xx 客户端错误 | 400 - 499 | 请求包含错误或无法处理 | 校验错误响应体是否符合错误契约 |
| 5xx 服务端错误 | 500 - 599 | 服务端处理失败 | 校验是否返回了通用的错误结构 |
3.3.2 核心状态码详解
- 200 OK:请求成功。GET 请求返回资源,POST 请求返回创建结果。
- 201 Created:资源创建成功。通常用于 POST 请求,响应头应包含
Location指向新资源。 - 204 No Content:请求成功,但无返回内容。常用于 DELETE 操作。
- 400 Bad Request:请求格式错误或参数校验失败。契约校验应返回明确的错误信息。
- 401 Unauthorized:未提供或提供了无效的身份凭证。
- 403 Forbidden:身份有效,但无权限访问资源。
- 404 Not Found:请求的资源不存在。
- 422 Unprocessable Entity:请求体语义错误,常用于校验失败场景。
- 500 Internal Server Error:服务端内部错误,通常不包含具体错误细节。
Go 代码示例: 在 Go 中,使用
http.StatusOK 等常量来引用状态码,避免硬编码数字。
package main
import "net/http"
func handleUserGet(w http.ResponseWriter, r *http.Request) {
// 模拟校验逻辑
if r.URL.Query().Get("id") == "" {
http.Error(w, `{"error":"missing id"}`, http.StatusBadRequest)
return
}
// 处理成功
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"id":"123","name":"Alice"}`))
}