3. REST API 基础回顾:RESTful 设计原则、HTTP 方法、状态码与资源路径

在进入契约压测之前,必须重新校准对 REST API 核心要素的理解。REST(Representational State Transfer)是一种架构风格,而非协议;它基于 HTTP 协议,将服务端数据抽象为资源,并通过统一的接口进行操作。本节将系统回顾 RESTful 设计的四大支柱:设计原则、HTTP 方法、状态码语义以及资源路径规范。

3.1 RESTful 六大设计原则

REST 架构风格由 Roy Fielding 在其博士论文中提出,核心约束如下:

原则 说明 违反示例
客户端-服务器 分离用户界面与数据存储,提升可移植性与可扩展性 在服务端直接渲染 HTML 模板并管理 Session
无状态 每个请求包含所有必要信息,服务端不保存客户端上下文 使用服务端 Session 存储用户登录状态
可缓存 响应隐式或显式标记为可缓存或不可缓存,减少网络交互 所有 GET 请求返回 Cache-Control: no-store
统一接口 通过标准 HTTP 方法操作资源,使用超媒体作为应用状态引擎 使用 /getUser、/deleteUser 等 RPC 风格路径
分层系统 客户端无法直接感知是否与最终服务器或中间层通信 客户端硬编码负载均衡器 IP 并依赖其直接返回
按需代码(可选) 服务器可临时扩展客户端功能(如返回 JavaScript 脚本) 强制客户端下载并执行服务端推送的二进制插件
契约压测视角: 无状态原则是压测的基础。如果 API 依赖服务端 Session,压测工具必须处理 Cookie 或 Token 传递,这会增加脚本复杂度并影响测试结果的纯净度。优先设计无状态 API。

3.2 HTTP 方法与 CRUD 映射

RESTful API 通过 HTTP 方法表达对资源的操作意图。以下是标准映射关系:

HTTP 方法 CRUD 操作 幂等性 安全性 请求体 响应体
GET 读取(Read) 是 是 无 资源表示
POST 创建(Create) 否 否 资源属性 新资源标识
PUT 全量替换(Update) 是 否 完整资源 更新后资源
PATCH 部分更新(Partial Update) 否 否 差异字段 更新后资源
DELETE 删除(Delete) 是 否 无 空或状态
幂等性关键: 在压测中,幂等接口(GET、PUT、DELETE)可以安全地重复发送请求而不改变服务端状态。非幂等接口(POST)需要额外处理重复创建问题,通常通过客户端生成唯一 ID(幂等键)来解决。

3.3 HTTP 状态码语义

状态码是服务端对请求处理结果的标准化反馈。契约压测中,断言的核心就是验证状态码是否符合预期。以下是 REST API 中最常用的状态码分类:

  • 2xx 成功
    • 200 OK:GET、PUT、PATCH 成功返回
    • 201 Created:POST 成功创建资源
    • 204 No Content:DELETE 成功,无返回体
  • 3xx 重定向
    • 301 Moved Permanently:资源永久迁移
    • 304 Not Modified:条件 GET,资源未变更
  • 4xx 客户端错误
    • 400 Bad Request:请求格式错误或参数无效
    • 401 Unauthorized:未提供认证信息
    • 403 Forbidden:已认证但无权限
    • 404 Not Found:资源不存在
    • 409 Conflict:资源当前状态冲突(如重复创建)
    • 422 Unprocessable Entity:请求体语义验证失败
    • 429 Too Many Requests:触发限流
  • 5xx 服务端错误
    • 500 Internal Server Error:通用服务端异常
    • 502 Bad Gateway:上游服务不可用
    • 503 Service Unavailable:服务过载或维护中
    • 504 Gateway Timeout:上游服务超时
压测陷阱: 不要将 429 或 503 视为测试失败。在压测中,这些状态码是系统限流或过载的正常信号,应作为性能指标记录,而非断言错误。契约压测应区分“功能错误”与“容量反馈”。

3.4 资源路径设计规范

资源路径(Endpoint)是 REST API 的对外接口。良好的路径设计能提升 API 的可读性和可维护性。以下是核心规范:

规范 正确示例 错误示例
使用名词复数 /users /getUser、/userList
层级关系用斜杠 /users/{id}/orders /usersOrders?id={id}
查询参数用于过滤/排序 /users?status=active&sort=created_at /users/active、/users/sortByDate
使用小写字母和连字符 /order-items /orderItems、/Order_Items
避免动词 POST /users/{id}/activate POST /activateUser
版本号放在前缀 /v1/users /users/v1、/users?version=1

3.5 REST API 请求-响应流程(SVG)

下图展示了一个完整的 REST API 调用流程,从客户端发起请求到服务端返回响应,涵盖方法、路径、状态码与资源状态变化:

REST API 请求-响应流程 REST API 请求-响应流程 客户端 服务端 资源存储 POST /v1/users {"name":"Alice","email":"a@b.com"} 验证请求体 写入资源 返回 ID 201 Created {"id":123,"name":"Alice"} 关键契约要素 方法: POST → 创建资源 路径: /v1/users → 资源集合 状态码: 201 → 创建成功

3.6 资源路径层级与集合关系(SVG)

REST API 的资源路径通常反映数据模型的层级关系。下图展示了一个典型电商系统的资源路径设计:

资源路径层级与集合关系 资源路径层级与集合关系 /v1 /users /products /orders /users/{id} /products/{id} /orders/{id} /orders/{id}/items 集合 → 单个资源 → 子集合

3.7 契约压测中的 REST 验证要点

在后续章节的压测脚本编写中,以下 REST 要素将作为契约断言的核心:

  • 方法验证: 确保压测请求使用正确的 HTTP 方法(如创建用户必须用 POST,而非 GET)。
  • 路径验证: 检查 URL 是否符合资源路径规范,避免因路径错误导致 404。
  • 状态码验证: 断言响应状态码与预期一致(如 POST 返回 201,DELETE 返回 204)。
  • 响应体验证: 验证 JSON 结构、字段类型、必填字段是否存在。
  • 幂等性验证: 对 PUT/DELETE 重复发送请求,验证状态不变。
Go 代码示例: 使用 net/http 发起 REST 请求并验证状态码
package main

import (
    "fmt"
    "net/http"
)

func main() {
    // 创建用户(POST)
    resp, err := http.Post("https://api.example.com/v1/users", "application/json", nil)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    // 契约断言:状态码必须为 201
    if resp.StatusCode != http.StatusCreated {
        fmt.Printf("契约失败:期望 201,实际 %d\n", resp.StatusCode)
    } else {
        fmt.Println("契约通过:用户创建成功")
    }
}