1. 接口设计基础:RESTful 架构、HTTP 方法、状态码设计

接口设计,说白了就是让不同的系统能好好说话。在我十多年的从业经历里,见过太多因为接口设计不规范引发的线上事故。合规治理的第一步,就是把接口基础打扎实。

1.1 RESTful 架构:不只是 URL 风格

很多人觉得 RESTful 就是“把 URL 写成名词,用动词指代操作”。这个理解有点浅了。我个人习惯把 REST 理解成一种设计哲学,它要求我们以资源为中心来思考问题。

RESTful 六大核心原则:
  • 客户端-服务器分离 —— 职责解耦,前端只管展示,后端只管数据
  • 无状态 —— 每次请求都携带全部上下文,服务端不保留会话状态
  • 可缓存 —— 响应中明确标记资源能否缓存、缓存多久
  • 统一接口 —— 用固定的 HTTP 方法操作资源,用标准状态码反馈结果
  • 分层系统 —— 客户端不知道它直接连的是源服务器还是中间层
  • 按需代码(可选) —— 服务器可以传一段脚本让客户端执行

为什么一定要强调无状态?我年轻的时候在一个电商平台做过一个购物车接口,把用户数据保存在服务端 Session 里。结果每次部署新版本,所有用户的购物车都丢了。从那时起,我就强迫自己把状态全部推到客户端,服务端只管处理请求。

我的一个经验:
如果你在设计接口时还在纠结“这个操作该用 PUT 还是 PATCH”,那你已经走在正确的路上了。怕的是直接把 POST 一把梭,所有操作都往里塞。

1.2 HTTP 方法:动词的正确用法

HTTP 协议定义了若干方法,但我们最常打交道的只有五个。你别看它们少,用错了照样出事。

方法 用途 幂等 安全性
GET 获取资源 是 是(不改变服务器状态)
POST 创建资源 否 否
PUT 完全替换资源 是 否
PATCH 部分更新资源 否(通常不幂等) 否
DELETE 删除资源 是 否

关于幂等这件事,我补充两句。一个请求发出去,如果网络超时了,客户端重试以后后端应该能保证结果一致。GET、PUT、DELETE 天生支持幂等,但 POST 不行。如果你用 POST 去创建订单,你的客户端必须做好去重防护。

举个具体例子:

# 好的设计
GET    /api/v1/users/{id}       # 获取用户信息
POST   /api/v1/users             # 创建新用户
PUT    /api/v1/users/{id}        # 全量更新用户(传完整对象)
PATCH  /api/v1/users/{id}        # 部分更新(只传要改的字段)
DELETE /api/v1/users/{id}        # 删除用户

# 不好的设计(我对这种深恶痛绝)
GET    /api/v1/getUser?id=123
POST   /api/v1/createUser
POST   /api/v1/updateUser
POST   /api/v1/deleteUser
我曾经踩过的一个坑:
有一个微服务为了图省事,把所有操作都用了 POST。后来审计时发现,根本无法判断某个接口到底做了什么事。想查日志都查不清,因为所有请求方法都是 POST。合规治理来了,第一件事就是要求把方法改对。

1.3 状态码设计:别只回 200 和 500

你想想看,状态码就是接口的方言。方言太乱,别人就听不懂你在说什么。很多团队只用了 200(成功)和 500(服务器异常),这等于没用心设计。

我建议把状态码分为三大类:

类别 常见状态码 使用场景
2xx 成功 200 201 204 200 表示正常返回数据;201 表示创建成功(配合 Location 头);204 表示删除成功,响应体为空
4xx 客户端错误 400 401 403 404 409 422 400 参数错误;401 未认证;403 无权限;404 资源不存在;409 资源冲突;422 请求体格式错误
5xx 服务端错误 500 502 503 500 内部错误;502 网关超时;503 服务不可用
还记得我之前说的购物车教训吗?
后来我做了一个规则:状态码必须大于等于 400 时,返回体中必须附带 error 字段,写上人能读懂的问题描述。 你不能只扔一个 400 就让调用方自己猜。

你以为光有状态码就够了吗?不够。我曾在一个金融项目中见过,4xx 写了 "参数错误" 但关键字段是空的——结果排障耗了三个小时。那段代码是我同事写的,但这次事件以后,我带头立了一个规范:所有 4xx 响应都要详细标注具体哪个字段错了、期望什么格式。

// 一个规范的错误响应体
{
    "error": {
        "code": "VALIDATION_FAILED",
        "message": "请求参数校验不通过",
        "details": [
            {
                "field": "email",
                "reason": "格式无效,期望:user@example.com"
            }
        ]
    }
}

1.4 一张图看懂接口设计基础

接口设计基础架构 接口设计基础 RESTful 架构 HTTP 方法 状态码设计 统一接口 无状态 可缓存 GET POST PUT PATCH DELETE 2xx 成功 4xx 客户端错 5xx 服务端错 核心原则: 用对 HTTP 方法 + 选对状态码 + 遵循 RESTful 风格 = 规范的可治理接口
状态码设计的一个小建议:
别把业务错误码和 HTTP 状态码混用。曾经有个团队,用 200 表示 "业务成功",用 200 加一个 error 字段表示 "业务失败"。结果网关层记录的都是 200,全线告警全废。后来我要求他们 所有业务逻辑层面的错误——包括校验、权限、冲突——都必须使用 4xx 状态码,网关才能正确统计失败率。