RESTful API 与 OpenAPI 设计实战教程:从规范到文档
2026-06-12 14:22:54
RESTful API 与 OpenAPI 设计实战教程
适用读者:后端开发者、技术负责人,需要设计规范、可维护的 API。
预计学习时长:55 分钟 · 含完整 API 设计练习
学习目标
完成本教程后,你将能够:
- 运用 REST 原则设计清晰、一致的 API 资源模型
- 制定团队级 URL 命名、状态码、错误响应规范
- 使用 OpenAPI 3.1 编写结构化 API 文档
- 配置 Swagger UI / Scalar 自动生成可交互文档
- 从 OpenAPI spec 生成客户端 SDK 和 Mock 服务
前置知识
- HTTP 协议基础(Method、Status Code、Header)
- JSON 数据格式
- 基本的后端开发经验
第一章:REST 设计原则
1.1 核心概念
REST(Representational State Transfer)以资源(Resource) 为中心,通过 HTTP 方法操作资源状态:
| HTTP 方法 | 操作 | 幂等 | 安全 |
|---|---|---|---|
| GET | 查询 | 是 | 是 |
| POST | 创建 | 否 | 否 |
| PUT | 全量更新 | 是 | 否 |
| PATCH | 部分更新 | 否 | 否 |
| DELETE | 删除 | 是 | 否 |
1.2 URL 设计规范
# 好的设计
GET /api/v1/users # 用户列表
POST /api/v1/users # 创建用户
GET /api/v1/users/{id} # 获取用户
PATCH /api/v1/users/{id} # 更新用户
DELETE /api/v1/users/{id} # 删除用户
GET /api/v1/users/{id}/orders # 用户的订单(嵌套资源)
# 反模式
GET /api/v1/getUserById?id=1 # 动词在 URL 中
POST /api/v1/user/delete # 用 POST 做删除
GET /api/v1/users/list/all # 冗余路径
1.3 命名约定
- 资源名用复数名词:
/users而非/user - 小写字母 + 连字符:
/blog-posts - 避免深层嵌套(不超过 2 层):
/users/{id}/orders✓,/users/{id}/orders/{oid}/items/{iid}✗ - 过滤、排序、分页用 Query Parameter
GET /api/v1/articles?status=published&sort=-created_at&page=2&limit=20
第二章:请求与响应规范
2.1 统一响应格式
{
"data": {
"id": "usr_123",
"name": "张三",
"email": "zhang@example.com"
},
"meta": {
"request_id": "req_abc123"
}
}
列表响应:
{
"data": [
{ "id": "usr_123", "name": "张三" },
{ "id": "usr_456", "name": "李四" }
],
"meta": {
"page": 1,
"limit": 20,
"total": 156,
"total_pages": 8
}
}
2.2 错误响应
{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求参数校验失败",
"details": [
{
"field": "email",
"message": "邮箱格式不正确"
}
]
},
"meta": {
"request_id": "req_abc123"
}
}
2.3 状态码使用
| 状态码 | 场景 |
|---|---|
| 200 | 成功(GET、PATCH、PUT) |
| 201 | 创建成功(POST) |
| 204 | 删除成功,无返回体 |
| 400 | 请求参数错误 |
| 401 | 未认证 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 409 | 冲突(如重复创建) |
| 422 | 语义错误(校验失败) |
| 429 | 请求过于频繁 |
| 500 | 服务器内部错误 |
避免滥用:不要用 200 返回错误信息;不要用 500 表示业务逻辑错误。
第三章:OpenAPI 3.1 实战
3.1 基本结构
openapi: 3.1.0
info:
title: 用户管理 API
version: 1.0.0
description: 用户 CRUD 与认证接口
contact:
name: API 团队
email: api@example.com
servers:
- url: https://api.example.com/v1
description: 生产环境
- url: http://localhost:3001/v1
description: 本地开发
paths:
/users:
get:
summary: 获取用户列表
operationId: listUsers
tags: [Users]
parameters:
- name: page
in: query
schema:
type: integer
default: 1
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
post:
summary: 创建用户
operationId: createUser
tags: [Users]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: 创建成功
content:
application/json:
schema:
$ref: '#/components/schemas/UserResponse'
'422':
description: 校验失败
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
User:
type: object
required: [id, name, email]
properties:
id:
type: string
example: usr_123
name:
type: string
minLength: 1
maxLength: 50
email:
type: string
format: email
created_at:
type: string
format: date-time
CreateUserRequest:
type: object
required: [name, email]
properties:
name:
type: string
minLength: 1
maxLength: 50
email:
type: string
format: email
UserResponse:
type: object
properties:
data:
$ref: '#/components/schemas/User'
UserListResponse:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
meta:
$ref: '#/components/schemas/PaginationMeta'
PaginationMeta:
type: object
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
total_pages:
type: integer
ErrorResponse:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
details:
type: array
items:
type: object
properties:
field:
type: string
message:
type: string
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- bearerAuth: []
3.2 生成文档站点
# 使用 Scalar(推荐,UI 现代)
npx @scalar/cli document openapi.yaml --output docs/
# 或使用 Swagger UI
npx swagger-ui-watcher openapi.yaml
3.3 代码优先 vs 设计优先
| 方式 | 工具 | 适用 |
|---|---|---|
| 设计优先 | Stoplight、Apifox | 前后端并行开发 |
| 代码优先 | SpringDoc、Hono zod-openapi | 后端主导 |
代码优先示例(Hono):
import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
import { z } from 'zod';
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
const listUsersRoute = createRoute({
method: 'get',
path: '/users',
responses: {
200: {
content: { 'application/json': { schema: z.array(UserSchema) } },
description: '用户列表',
},
},
});
const app = new OpenAPIHono();
app.openapi(listUsersRoute, (c) => {
return c.json([{ id: '1', name: '张三', email: 'a@b.com' }]);
});
app.doc('/doc', {
openapi: '3.1.0',
info: { title: 'API', version: '1.0.0' },
});
第四章:版本策略与兼容性
4.1 URL 版本(推荐)
/api/v1/users
/api/v2/users
直观、易路由、易废弃。
4.2 向后兼容原则
安全变更(不需要新版本):
- 添加可选字段
- 添加新端点
- 添加可选 Query Parameter
破坏性变更(需要新版本):
- 删除/重命名字段
- 修改字段类型
- 修改 URL 路径
- 修改认证方式
4.3 废弃流程
paths:
/users/legacy:
get:
deprecated: true
description: |
已废弃,请使用 GET /users。
将于 2026-12-31 下线。
responses:
'200':
headers:
Sunset:
schema:
type: string
description: 下线日期
Deprecation:
schema:
type: string
第五章:安全与限流
5.1 认证
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
apiKey:
type: apiKey
in: header
name: X-API-Key
5.2 限流响应
responses:
'429':
description: 请求过于频繁
headers:
Retry-After:
schema:
type: integer
description: 建议重试等待秒数
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
练习 / 作业
- 为一个「文章管理」系统设计完整 RESTful API(至少 5 个端点),绘制资源关系图。
- 编写 OpenAPI 3.1 YAML,包含 CRUD + 分页 + 错误响应。
- 用 Scalar 或 Swagger UI 渲染文档,在浏览器中测试接口。
- 定义团队的 API 错误码规范(至少 10 个业务错误码)。
- 进阶:用
openapi-generator从 spec 生成 TypeScript 客户端 SDK。
FAQ
Q:REST 和 GraphQL 怎么选?
A:REST 适合资源清晰、缓存友好、团队熟悉的场景。GraphQL 适合前端需要灵活查询、减少请求次数的场景。可共存。
Q:PUT 和 PATCH 区别?
A:PUT 提交完整资源(缺失字段视为 null/默认值),PATCH 只提交变更字段。推荐 PATCH 做更新。
Q:分页用 offset 还是 cursor?
A:Offset 简单,适合小数据集。Cursor(游标)适合大数据集和实时数据,避免翻页漂移。
Q:OpenAPI 和 Swagger 什么关系?
A:Swagger 是早期名称,OpenAPI 是捐赠给 Linux 基金会的开放标准。Swagger UI 是 OpenAPI 的文档渲染工具。
Q:如何保证文档和代码同步?
A:优先代码优先(注解生成 spec),CI 中校验 spec 与实现一致性。
小结
好的 API 设计是团队效率的基石。遵循 REST 资源模型、统一响应格式、正确使用状态码,用 OpenAPI 3.1 维护活文档。核心原则:一致性优于灵活性,明确性优于简洁性,向后兼容是默认策略。