RESTful API 基础:概念、设计原则与 HTTP 方法
说到 RESTful API,很多初学者第一反应就是“用 HTTP 动词操作数据”。这话没错,但想真正用好,得先搞清楚它背后的设计哲学。我刚开始接触分布式系统时,也以为 REST 就是 CRUD 的换皮,后来被一个老前辈问住:“你把订单状态改成已支付,用 PUT 还是 PATCH?” 那次之后,我才老老实实地把 Fielding 的啃了一遍。
什么是 REST?
REST(Representational State Transfer)是一种架构风格,不是协议也不是标准。它由 Roy Fielding 在 2000 年的博士中提出。说白了,它教你如何用 Web 现有的机制——URI、HTTP 方法、状态码、内容协商——来组织分布式系统的交互。
我个人理解,REST 的核心就一句话:把业务实体抽象成资源,通过统一的接口操作它们。你想想看,浏览器里的网页就是资源,点击链接(URI)查看,提交表单(POST)创建,就是这个道理。
关键概念:REST 是面向资源的,不是面向操作的。不要设计 /api/createUser 这样的接口,而应该是 POST /api/users。
五个设计原则
我把它归纳成五个关键词:资源、URI、无状态、统一接口、超媒体驱动。前四个在实际项目中几乎天天碰到,最后一个目前用得较少,但理解它有助于设计真正 RESTful 的系统。
1. 资源(Resource)
资源就是业务实体,比如用户、订单、商品。每个资源有一个唯一标识——URI。我见过很多团队把“操作”当成资源,比如 /getUserById,这是 RPC 思维。正确的做法是把“人”抽象成资源,然后通过 HTTP 方法表达操作。
2. URI 设计
URI 应该清晰、可读、可预测。我习惯用名词复数,层级用斜杠分隔,查询参数用于筛选/排序/分页。
- 好的例子:
GET /api/users/{id}、GET /api/users?role=admin - 坏例子:
GET /api/getUser?id=123、GET /api/deleteUser
个人小技巧:URI 中不要出现动词,动词应该交给 HTTP 方法。如果非要在 URI 里表达动作,比如“激活用户”,可以考虑用 POST /api/users/{id}/activate,但这其实已经有点 RPC 味道了,更纯粹的做法是用状态变更资源:PATCH /api/users/{id} 带上 status: active。
3. 无状态(Stateless)
每个请求必须包含所有必要信息,服务器不保存客户端上下文。我最开始做电商系统时,把用户购物车信息放在 session 里,结果一水平扩展就出问题——用户请求打到了不同服务器,购物车丢了。后来我把 session 数据存到 Redis 共享,但按 REST 要求,应该是客户端自己维护会话状态(比如用 附带身份信息)。
无状态的好处是显而易见的:服务器可以随意伸缩,任何一个节点都能处理任意请求。坏处嘛,请求体积会变大(每次都得带 等信息),但这点开销和运维的便利性比起来,完全不值一提。
4. 统一接口(Uniform Interface)
这是 REST 最核心也是最容易被忽略的原则。它要求接口范式是固定的:资源标识(URI)、资源表述(JSON/XML)、自描述消息(Content-Type)、超媒体驱动(HATEOAS)。实际项目中,我很少看到有人实现 HATEOAS,但前三点一定要遵守。
统一接口的好处是:客户端和服务端解耦,只要接口契约不变,双方都可以独立演化。我曾经在一个遗留系统中接手过六种不同的接口风格(有 JSON-RPC、有 SOAP、有自定义协议),维护成本极高。后来全部改成了 RESTful 统一接口,新人上手时间从两周缩短到两天。
5. 超媒体驱动(HATEOAS)
给客户端返回的资源里要包含超链接,引导客户端进行下一步操作。比如获取一个订单后,响应里附上 “pay”: “/api/orders/123/pay” 的链接。大部分业务系统不会做到这个程度,但如果你在构建开放平台,这非常有用——客户端不需要硬编码 URI 结构。
HTTP 方法的使用
REST 通过 HTTP 方法(动词)来表达对资源的操作。我整理了一张表格,你可以直接拿来参考:
| HTTP 方法 | 操作 | 幂等 | 安全 | 典型场景 |
|---|---|---|---|---|
| GET | 获取资源 | 是 | 是 | 查询列表、查看详情 |
| POST | 创建资源 | 否 | 否 | 新建用户、提交订单 |
| PUT | 全量替换资源 | 是 | 否 | 更新用户全部信息 |
| PATCH | 部分更新资源 | 否 | 否 | 修改用户邮箱、修改订单状态 |
| DELETE | 删除资源 | 是 | 否 | 删除用户、取消订单 |
避坑指南:不要用 GET 修改状态!我见过有人为了省事,用 GET /api/deleteUser?id=123,这个请求被搜索引擎爬虫抓到了,直接把用户删了……血泪教训。还有,POST 和 PUT 的幂等性经常被搞混。简单记:PUT 是全量替换,客户端传什么服务器就存什么,多次请求结果一样;POST 通常用于追加,每次请求都会创建新资源或改变状态。
状态码的正确使用
很多开发者只记得 200、404、500,其实 HTTP 状态码是 REST 接口的一部分。我习惯按类别分:
- 2xx:成功(200 OK、201 Created、204 No Content)
- 3xx:重定向(304 Not Modified 可用于缓存优化)
- 4xx:客户端错误(400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found、409 Conflict)
- 5xx:服务器错误(500 Internal Server Error、503 Service Unavailable)
举个例子:创建资源成功后,一定要返回 201,而不是 200 然后 body 里写“success”。你可以想想看,如果客户端用标准 HTTP 库调用,201 和 200 的处理逻辑可能完全不同——201 下客户端可以读取 Location 头获取新资源 URI。
知识体系结构
下面这张图概括了 RESTful API 的核心概念和设计要点,你可以把它当作学习的路线图:
总结
RESTful API 设计的本质是用 Web 自身的语义来构建分布式系统。不要把它当成一种“规范”,它更多是给你一套思考框架。我在项目中反复踩坑后的心得是:先把资源抽象好,然后再看 HTTP 方法怎么映射,最后用状态码和后端逻辑把状态管理理顺。只要能做到这三点,接口的维护成本会大大降低。
嗯,记住一句话:REST 不是银弹,但它是目前 Web 服务最流行的架构风格。理解它的设计原则,能帮你做出更合理的选择——这也是我们后面对比 gRPC 时的基础。