好 API 的标准是什么
一个好的 API 应该是"自解释"的——开发者不需要反复查阅文档就能理解如何使用。以下是衡量 API 质量的几个维度:
- 一致性:命名规范、URL 结构、错误格式在全 API 中保持一致
- 可预测性:用户看到
GET /users就知道能获取用户列表,看到POST /users就知道能创建用户 - 简洁性:不要暴露内部实现细节。API 是给调用者用的,不是数据库的镜像
- 健壮性:合理的错误处理、幂等性保障、向后兼容
RESTful 设计原则
RESTful API 的核心原则可以归纳为四点:
资源导向(Resource-Oriented)
URL 应该表示资源(名词),而不是操作(动词):
# 好的设计
GET /articles # 获取文章列表
GET /articles/42 # 获取单篇文章
POST /articles # 创建文章
PUT /articles/42 # 更新文章(全量)
PATCH /articles/42 # 更新文章(部分)
DELETE /articles/42 # 删除文章
# 不好的设计
GET /getArticles
POST /createArticle
GET /deleteArticle?id=42
正确使用 HTTP 方法与状态码
# HTTP 方法语义
GET - 获取资源,安全且幂等
POST - 创建资源,非幂等
PUT - 全量替换资源,幂等
PATCH - 部分更新资源,非幂等
DELETE - 删除资源,幂等
# HTTP 状态码速查
200 OK - 请求成功
201 Created - 资源创建成功
204 No Content - 删除成功,无返回体
400 Bad Request - 请求参数错误
401 Unauthorized - 未认证
403 Forbidden - 无权限
404 Not Found - 资源不存在
409 Conflict - 资源冲突(如重复创建)
422 Unprocessable - 参数验证失败
429 Too Many - 触发限流
500 Internal Error- 服务器内部错误
版本管理策略
API 版本管理有三种主流方式,各有优劣:
- URL 路径版本:
/v1/users、/v2/users。最直观,但 URL 变长,被认为不够"RESTful" - 请求头版本:
Accept: application/vnd.api.v1+json。URL 干净,但调试不方便 - 查询参数版本:
/users?version=1。简单但容易遗漏
我的推荐是 URL 路径版本。虽然不够纯粹,但它是最实用、最容易被团队理解和调试的方案。Stripe、GitHub、Twilio 等知名 API 都采用这种方式。
# 版本演进示例
GET /v1/users # v1: 返回基本用户信息
GET /v2/users # v2: 增加了 avatar、social_links 字段
GET /v1/users # v1 仍然可用,给调用者迁移时间
错误响应格式:RFC 7807 Problem Details
不要返回纯文本错误信息。使用 RFC 7807 定义的标准化错误格式:
// HTTP 422 Unprocessable Entity
{
"type": "https://api.example.com/errors/validation-error",
"title": "Validation Error",
"status": 422,
"detail": "请求参数验证失败",
"instance": "/v1/users",
"errors": [
{
"field": "email",
"message": "邮箱格式不正确",
"code": "invalid_format"
},
{
"field": "age",
"message": "年龄必须在 0 到 150 之间",
"code": "out_of_range"
}
]
}
这个格式的好处:type 是一个文档 URL,点击可以看到错误的详细说明;errors 数组可以包含多个字段级别的错误,方便前端直接绑定到表单控件。
分页设计:游标 vs 偏移
分页是 API 设计中最容易出问题的地方。两种主流方案:
偏移分页(Offset Pagination)
# 请求
GET /v1/articles?offset=20&limit=10
# 响应
{
"data": [...],
"pagination": {
"offset": 20,
"limit": 10,
"total": 157
}
}
优点:实现简单,支持跳页。缺点:在高并发写入场景下,数据可能重复或遗漏。
游标分页(Cursor Pagination)
# 请求
GET /v1/articles?cursor=eyJpZCI6NDJ9&limit=10
# 响应
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6NTJ9",
"has_more": true
}
}
游标分页使用一个不透明的标记(通常是最后一条记录的 ID 编码)来确定下一页的起点。它不受并发写入影响,性能更好(不需要 COUNT(*)),但不支持跳页。
选择建议:数据量大且实时性高(如消息列表、动态流)用游标分页;管理后台、报表系统用偏移分页。
认证与鉴权
JWT(JSON Web Token)
JWT 是目前最流行的无状态认证方案:
# 请求头
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# JWT 结构(Base64 编码)
# Header: {"alg": "HS256", "typ": "JWT"}
# Payload: {"sub": "123", "name": "Alice", "iat": 1716931200, "exp": 1717017600}
# Signature: HMAC-SHA256(Header + "." + Payload, secret)
JWT 的使用原则:
- 设置合理的过期时间:Access Token 15 分钟,Refresh Token 7 天
- 不要存储敏感信息:Payload 只是 Base64 编码,任何人都可以解码
- 使用 HTTPS:防止 Token 在网络传输中被截获
OAuth 2.0
当你的 API 需要第三方应用访问用户数据时,使用 OAuth 2.0。典型的授权码流程:
# 1. 用户点击"使用 Google 登录"
# 2. 重定向到授权页面
GET https://accounts.google.com/o/oauth2/auth?
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/callback&
response_type=code&
scope=email profile
# 3. 用户同意后,回调带回授权码
# 4. 用授权码换取 Token
POST https://oauth2.googleapis.com/token
{
"code": "AUTH_CODE",
"client_id": "...",
"client_secret": "...",
"grant_type": "authorization_code"
}
速率限制
不加限制的 API 是脆弱的。常见的限流策略:
# 响应头中返回限流信息
X-RateLimit-Limit: 1000 # 时间窗口内允许的总请求数
X-RateLimit-Remaining: 987 # 剩余可用请求数
X-RateLimit-Reset: 1716934800 # 窗口重置的 Unix 时间戳
# 超限时返回
HTTP 429 Too Many Requests
Retry-After: 60
限流粒度应该分三层:全局(整个 API)、用户级(每个认证用户)、IP 级(未认证请求)。
API 文档与 OpenAPI/Swagger
没有文档的 API 等于不存在。OpenAPI 规范(原 Swagger)是 API 文档的事实标准:
# openapi.yaml
openapi: 3.0.3
info:
title: Article API
version: 1.0.0
paths:
/v1/articles:
get:
summary: 获取文章列表
parameters:
- name: status
in: query
schema:
type: string
enum: [draft, published, archived]
responses:
'200':
description: 成功返回文章列表
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Article'
工具推荐:
- Swagger UI:将 OpenAPI 规范渲染为交互式文档页面
- Redoc:更美观的文档生成器,适合对外 API
- Stoplight Studio:可视化的 API 设计工具
- Scalar:新一代 API 文档工具,交互体验优于 Swagger UI
总结
设计一个好的 API 需要的不是炫技,而是纪律。统一的 URL 命名、一致的错误格式、合理的分页策略、清晰的文档——这些看似基础的事情,恰恰是大多数 API 做得不好的地方。从今天开始,给你的 API 加上 RFC 7807 错误格式,写上 OpenAPI 文档,你的调用者会感谢你的。