接口规范基础:RESTful API 设计原则
聊到 Secrets Manager,绕不开的就是接口规范。你想想看,一个云上的密码管理服务,如果 API 设计得乱七八糟,谁敢用?
我个人习惯,在设计任何云服务之前,先把 RESTful 的规矩定好。这一章,咱们就聊聊这些规矩。我不会给你背书,会结合我踩过的坑来讲。
1. 什么是 REST?说白了就是资源导向
REST,全称 Representational State Transfer。翻译成人话就是:把一切业务抽象成“资源”,然后用标准的 HTTP 方法去操作它。
举个例子,对于 Secrets Manager,核心资源就是“密钥”(Secret)。我们所有操作,都应该围绕着这个“密钥”资源来展开。
我的经验: 刚开始做 API 时,我总习惯把操作直接写成动词,比如
/getSecret、/createSecret。后来发现,这种 RPC 风格的路由会让接口越来越乱,维护成本极高。改用 REST 后,一个 POST /secrets 就能搞定创建,清晰多了。
2. 核心原则:我就说几个最关键的
RESTful API 设计原则很多,但真正在项目里天天用的,无非这几个。
2.1 资源路径是名词,用复数
路径里只放名词,代表一个资源集合。千万别把动词塞进去。
正确:
GET /api/v1/secrets # 获取密钥列表
POST /api/v1/secrets # 创建密钥
GET /api/v1/secrets/{id} # 获取单个密钥
PUT /api/v1/secrets/{id} # 更新整个密钥
DELETE /api/v1/secrets/{id} # 删除密钥
错误(RPC 风格):
GET /api/v1/getSecret
POST /api/v1/createSecret
POST /api/v1/deleteSecretById
2.2 HTTP 方法就是操作动词
HTTP 协议自带的方法已经定义了明确的语义,不要自己造轮子。
| HTTP 方法 | 操作 | 幂等性 | Secrets Manager 场景示例 |
|---|---|---|---|
| GET | 读取资源 | 是 | 查询某个密钥的元数据 |
| POST | 创建资源(非幂等) | 否 | 创建一个新的密钥版本 |
| PUT | 完全替换资源 | 是 | 更新整个密钥的描述信息 |
| PATCH | 部分更新资源 | 否 | 仅更新密钥的轮转周期 |
| DELETE | 删除资源 | 是 | 删除一个废弃的密钥 |
注意: 别小看幂等性。我在公司内部推过一套接口规范,结果有个同事用 POST 做更新操作,导致客户端重试时,数据被重复插入。后来统一改为 PUT,问题迎刃而解。
2.3 状态码要表意准确
光返回 200 或 500 是不够的。状态码是服务端跟客户端沟通的第一语言。
- 200 OK:GET、PUT、PATCH 成功。
- 201 Created:POST 创建资源成功,通常在响应头里带上
Location指向新资源。 - 204 No Content:DELETE 成功,不需要返回消息体。
- 400 Bad Request:客户端请求不合法,比如参数缺失、格式错误。
- 401 Unauthorized:未认证,通常是你忘了带 。
- 403 Forbidden:认证了,但没权限。比如你想删一个别人的密钥。
- 404 Not Found:资源不存在。但有个技巧——在安全要求高的场景下,可以用 400 或 403 来替代 404,避免攻击者探测资源是否存在。
- 429 Too Many Requests:限流了。这种状态码在 Secrets Manager 里很常见,特别是被高频轮转的密钥。
- 5xx:服务端错误。别滥用,而且最好把错误细节脱敏后返回,方便排查。
3. 响应体设计:聊聊返回什么
我个人建议,统一用 JSON 格式。而且,响应体要结构化、可预测。
// 成功的响应示例
{
"status": "success",
"data": {
"secretId": "sec-abc123",
"name": "prod-db-password",
"versionId": "v1",
"lastRotatedAt": "2024-01-15T10:30:00Z"
},
"requestId": "req-xyz789"
}
// 失败的响应示例
{
"status": "error",
"error": {
"code": "INVALID_PARAMETER",
"message": "Secret name must be 1-255 characters long.",
"details": {
"field": "name",
"reason": "too_long"
}
},
"requestId": "req-xyz789"
}
关键点: 永远带上
requestId。无论是成功的请求还是失败的请求,这个 ID 能帮你跟客户端快速对齐日志,排查问题。
4. 分页:别想着一次返回所有数据
假设你的 Secrets Manager 里有 10 万个密钥,一次 GET /secrets 返回所有?
不现实。不仅客户端扛不住,服务端也会被拖垮。我建议用基于游标的分页,比传统 offset/limit 更稳定。
请求:
GET /api/v1/secrets?limit=50&cursor=eyJsYXN0SWQiOjEwMH0=
响应:
{
"data": [...],
"pagination": {
"nextCursor": "eyJsYXN0SWQiOjE0OX0=",
"hasMore": true
}
}
为什么要用游标?因为当数据在分页过程中被删除或添加时,offset/limit 可能导致数据重复或遗漏。游标只关心“上一次的最后一条”,稳定很多。
5. 版本控制:温和演进,不破坏兼容
API 上线后,不可能一成不变。怎么优雅地升级?
- 路径版本:
/api/v1/secrets、/api/v2/secrets。最简单,但会导致一套代码维护多份路由。 - 请求头版本: 用
Accept: application/vnd.secretsmanager.v2+json。更灵活,后端可以通过中间件做路由。
我个人倾向路径版本,因为一眼能看清版本。但如果你用微服务网关,请求头版本会更优雅。
6. RESTful API 设计核心要素
说了这么多,咱们用一张图来总结一下。
7. 安全和身份:不能忘的底线
Secrets Manager 是存机密的地方,接口安全必须拉满。
- HTTPS 是底线: 明文传输密码?不行。所有 API 必须强制走 HTTPS。
- 鉴权: 通常用 Bearer 或 。我建议不要自己做鉴权方案,直接用 OAuth 2.0 或云平台的 IAM 体系。
- 请求签名: 为了防止请求被篡改,我一般采用 HMAC-SHA256 对关键字段签名。比如 AWS 的 Signature V4,就是一个很成熟的参考。
避坑指南: 我曾经碰到过一个客户,把 直接写在公网请求的 URL 参数里。结果日志系统把 URL 全量记录了,Key 就泄漏了。后来我强制要求:所有敏感信息必须放在 HTTP 头里,而且日志要脱敏处理。
8. 总结一下这四个核心
- 资源抽象: 把一切业务抽象为资源,路径是名词。
- 方法语义: GET 读、POST 建、PUT 全量替换、PATCH 增量更新、DELETE 删除。
- 状态码与错误信息: 用标准状态码表达语义,错误信息要结构化、可读。
- 安全与进化: 鉴权、HTTPS、版本控制,一个都不能少。