知识点深化 · API 设计
RESTful API 设计:资源命名、状态码、版本化、认证与 OpenAPI 规范
前端和后端怎么约定?一个好 API 应该"看 URL 就知道操作什么,看状态码就知道结果"。这一页不讲大道理,直接给你一套能照着写的规范:URL 怎么命名、GET/POST/PUT/DELETE 怎么用、状态码什么时候返回、怎么版本化、怎么鉴权,并用 OpenAPI(Swagger) 把接口文档自动生成出来。
① 小白第一课怎么学(4 步走,约 80 分钟)
先抓住"资源"这个核心词,REST 就是围绕资源做操作。
1建立资源思维(15 分钟)
读②③:URL 表示"名词资源",HTTP 方法表示"动作"。
2记状态码表(20 分钟)
读④:2xx/4xx/5xx 各代表什么,常用的那几个。
3设计一个资源 CRUD(30 分钟)
跟着⑤把"订单"的增删改查接口设计出来。
4排错+刷题(15 分钟)
读⑥命名/状态码坑,做⑦⑩。
本课小目标学完你要能:① 用名词设计 URL;② 正确选用 HTTP 方法和状态码;③ 设计 API 版本化方案;④ 用 OpenAPI 描述一个接口。
② 一图看懂:RESTful 资源模型
读法:REST = URL 表示哪个资源(名词复数)+ HTTP 方法表示干什么 + 状态码表示结果。不要在 URL 里写动词。
③ 本质直觉:把网络当成"对资源的增删改查"
想象你在操作一个数据库,但通过网络。系统里有一堆"资源":用户、订单、文章。REST 就是约定一套统一的姿势去操作它们。
URL 是名词,不是动词:你要操作"订单"这个资源,URL 就叫 /orders。是查、是增、是删,靠 HTTP 方法区分,而不是写成 /getOrder、/createOrder。
用复数名词:/orders 是订单集合,/orders/123 是 123 号订单。嵌套表示从属:/users/1/orders 是用户 1 的订单。
方法对应动作:GET 查、POST 增、PUT/PATCH 改、DELETE 删。同样一个 /orders/123,用不同方法就是不同操作。这套约定的好处是:前端看到 DELETE /orders/123 立刻懂"删 123 号订单",不用翻文档。
为什么用复数名词因为 URL 指向的是"资源集合"这个概念。/user 和 /users 规范上用后者;单数 /orders/123 里的 123 才是单个资源。保持一致,团队就不用每次纠结。
④ 完整体系:命名、方法、状态码、版本化、OpenAPI
HTTP 方法语义
| 方法 | 语义 | 示例 | 幂等 |
| GET | 查询资源 | GET /orders?status=paid | 是 |
| POST | 新建资源 | POST /orders | 否 |
| PUT | 整体替换 | PUT /orders/123 | 是 |
| PATCH | 局部更新 | PATCH /orders/123 {status} | 否 |
| DELETE | 删除 | DELETE /orders/123 | 是 |
必记状态码
| 状态码 | 含义 | 何时返回 |
| 200 OK | 成功 | 查询/更新成功 |
| 201 Created | 已创建 | POST 新建成功,返回 Location |
| 204 No Content | 成功无返回体 | DELETE 成功 |
| 400 Bad Request | 参数错 | 请求体格式/字段不合法 |
| 401 Unauthorized | 未认证 | 没带 token 或 token 失效 |
| 403 Forbidden | 无权限 | 已登录但不能操作此资源 |
| 404 Not Found | 资源不存在 | 查/删不存在的 id |
| 409 Conflict | 冲突 | 唯一约束重复(如用户名已存在) |
| 422 Unprocessable | 语义错 | 格式对但业务不通过 |
| 500 Internal Error | 服务器错 | 后端崩溃 |
URL 命名规范
| 做法 | 推荐 | 不推荐 |
| 名词复数 | /orders | /getOrders |
| 小写连字符 | /order-items | /OrderItems |
| 嵌套 | /users/1/orders | 深层无限嵌套(≤2 层) |
| 过滤/分页 | ?page=2&size=20 | 塞进路径 |
版本化与认证
版本化:破坏性变更时用 /v1/orders、/v2/orders,老客户端继续用 v1。
认证:用 Bearer Token,放在请求头 Authorization: Bearer <token>。不要把 token 放 URL(会进日志)。
OpenAPI 片段(Swagger)
openapi: 3.0.0
paths:
/orders:
get:
summary: 查询订单列表
parameters:
- {name: status, in: query, schema: {type: string}}
responses:
'200':
description: 成功
content:
application/json:
schema: {type: array, items: {$ref: '#/components/schemas/Order'}}
FastAPI/Express 配 swagger 后,访问 /docs 自动出可交互文档。
⑤ 用法场景与典型例题
例1(设计 CRUD)为"订单"资源设计一套增删改查接口
URL 用名词,方法用动作。
① 查询列表:GET /orders?page=1。
② 查询单个:GET /orders/123。
③ 新建:POST /orders(返回 201)。
④ 全量更新:PUT /orders/123;局部更新:PATCH /orders/123。
⑤ 删除:DELETE /orders/123(返回 204)。
答案:围绕 /orders 和 /orders/{id} 五个方法,不用写动词 URL。
例2(状态码)前端提交了一个不存在的订单 id,后端应返回?
资源不存在。
① 该订单在数据库里没有。
② 应返回 404 Not Found,body 带错误说明。
③ 不要返回 200 空数组(那是"列表查无",不是"单资源不存在"),也不要 500。
答案:GET /orders/999 不存在 → 404。
例3(认证)用户没登录就访问 /orders,返回什么?已登录但不是自己的订单呢?
区分"没认证"和"没权限"。
① 没带 token / token 失效 → 401 Unauthorized(你是谁都不知道)。
② 带了有效 token,但这个订单属于别人 → 403 Forbidden(知道你是谁,但不让你动)。
答案:401=没登录;403=登录了但没权限。两者别混。
错误返回体统一格式建议所有错误返回 {"error": {"code": "NOT_FOUND", "message": "订单不存在"}},前端好统一处理。
⑥ 高频错误诊断(4 条)
错误 1:URL 里写动词常见 /getUser?id=1、/createOrder。REST 里动作该由 HTTP 方法表达,URL 只放名词。正确是 GET /users/1、POST /orders。
错误 2:状态码乱用,全返回 200失败也返回 200 + body 里写 success:false,让前端没法靠状态码判断。该 400/404/500 就老实返回对应码。
错误 3:把 token 放 URL query/orders?token=xxx 会被浏览器历史、Nginx 日志记录,泄露。放 Authorization 头。
错误 4:破坏性变更不做版本直接改 v1 接口的字段,老客户端全崩。破坏性变更新开 /v2/,v1 保留并标注废弃。
⑦ 考点真题演练(4 题)
考点分布
| 考法 | 出题形式 | 应对 |
| 命名 | 挑出不规范 URL | 名词复数,无动词 |
| 方法 | 操作选方法 | GET查 POST增 PUT改 DELETE删 |
| 状态码 | 场景选码 | 401/403/404 分清 |
| 认证 | token 放哪 | Authorization 头 |
真题基础1. 下列哪个 URL 符合 RESTful 规范?
真题中档2. 删除一个订单资源,应使用哪个方法?
真题中档3. 已登录用户试图访问别人的订单,应返回?
真题拔高4. POST 创建订单成功,最恰当的状态码是?
⑧ 必背命令/知识点卡
URL:名词复数 /orders,不写动词 小写连字符
方法:GET查 POST增 PUT改 PATCH局部 DELETE删 动作在方法里
成功:200 查改 / 201 新建 / 204 删除 别全用200
客户端错:400 参数 / 401 未登录 / 403 无权限 / 404 不存在 分清401和403
服务端错:500 内部错 别把业务错当500
认证:Authorization: Bearer <token> 不放URL
版本:/v1/ /v2/,破坏性变更才开新版 向后兼容
⑨ 应用输出:设计一套"文章"API 并出文档
场景:博客系统,需要文章的增删改查,且要给前端看文档
① 定资源:文章资源 = /articles。
② 列接口:GET /articles(分页+搜索)、GET /articles/{id}、POST /articles、PUT /articles/{id}、DELETE /articles/{id}。
③ 定状态码:POST 成功 201;删成功 204;文章不存在 404;未登录 401;非作者改文章 403。
④ 鉴权:除 GET 列表/详情外,写操作都要 Bearer Token,后端校验是否作者本人。
⑤ 写 OpenAPI:用 FastAPI 装饰器自动生成,访问 /docs 出 Swagger 交互页,前端可直接试调。
⑥ 版本:当前 /v1/articles;以后大改再开 /v2/。
口述设计思路"把文章当资源,URL 用名词复数,方法定动作,状态码如实返回结果,写操作加 token 鉴权,最后用 OpenAPI 自动出文档。"
⑩ 分层练习 15 题(基础 5 + 中档 5 + 拔高 5)
▍基础 5 题
基础1RESTful 的 URL 应该用名词还是动词?
名词(资源),动作由 HTTP 方法表达。
基础3201 状态码表示什么?
资源创建成功,通常是 POST 后返回。
基础4401 和 403 的区别?
401=未认证(没登录);403=已认证但无权限。
基础5token 应该放在哪里?
请求头 Authorization: Bearer xxx,不放 URL。
▍中档 5 题
中档6PATCH 和 PUT 的区别?
PUT 整体替换资源;PATCH 只更新部分字段。
中档7删除成功返回什么状态码?
204 No Content(无返回体)。
中档8分页参数放哪?
query string,如 /orders?page=2&size=20。
中档9客户端传的 JSON 缺必填字段,返回?
400 Bad Request,说明哪个字段错。
中档10为什么破坏性变更要加 /v2/?
保护老客户端不崩;v1 继续可用,新客户端用 v2。
▍拔高 5 题
拔高11PUT /users/1 重复发两次结果一样吗?这叫什么?
一样,叫幂等。PUT/DELETE/GET 都应幂等;POST 不是。
拔高12用户名已被注册,注册接口返回什么?
409 Conflict(唯一约束冲突),body 说明冲突字段。
拔高13OpenAPI 的价值是什么?
机器可读的接口描述,可自动生成文档、SDK、Mock,前后端契约清晰。
拔高14为什么不建议在 URL 里传动作如 /search?
搜索不是资源,但实在需要可接受;更 REST 的做法是 GET /resources?q=kw。保持资源导向。
拔高15后端内部错误 vs 业务规则不满足,状态码怎么分?
业务不满足(如余额不足)用 4xx(422/400)+ 业务码;真正代码崩溃/依赖挂了才用 500。
⑪ 记忆口诀 + 7 天复习计划
三句口诀
① URL 是名词复数,动作交给 HTTP 方法。
② 成功 200/201/204,客户端错 400/401/403/404。
③ token 放头不放 URL,破坏性变更开 v2。
| 天 | 任务 | 自检 |
| 第 1 天 | 读②③,画一遍资源模型 | 能分清名词和动词 |
| 第 2 天 | 背状态码卡 + 做基础 1-5 | 基础全对 |
| 第 3 天 | 设计文章 CRUD 五个接口 | 方法状态码对 |
| 第 4 天 | 做中档 6-10,区分 401/403/404 | 不混 |
| 第 5 天 | 做拔高 11-15 + 真题 4 题 | 会写 OpenAPI |
| 第 6-7 天 | 口述一套 API 设计规范,默写状态码表 | 不看资料全默对 |
← 返回 FDE 培养总览