别再假装你的API是RESTful了

2026-03-19 13 0

# 别再假装你的API是RESTful了

> 声明:本文不针对任何人,因为我们都曾天真地以为 PUT /users/123/update-profile 是什么高大上的设计。

## 写在前面

每次看到有人煞有介事地讨论"你的API不够RESTful"时,我就想起那个把curl当成PHP框架的下午。不是说RESTful不好,而是——咱们能不能先聊聊,什么是真正的RESTful?

作为一个写过几百个API、踩过无数坑的后端仔,今天我想聊聊API设计这件小事。不讲什么HATEOAS,什么Richardson成熟度模型,咱们就聊聊:怎么设计一个能用的、别人不会半夜打电话骂你的API。

## RESTful:这个锅我不背

首先,让我们达成一个共识:**大多数声称自己是RESTful的API,实际上连REST的边都没沾到。**

为什么这么说?让我问你几个问题:

- 你在URL里用了动词吗?`/users/123/update-profile`?恭喜你,这叫RPC,不是REST。
- 你用POST做一切事情吗?创建用POST,查询用POST,删除也用POST?嗯,你只是在用HTTP当传输协议,REST不背这个锅。
- 你的API有版本号吗?`/api/v1/users`?这没问题,但这跟RESTful没半毛钱关系。

**真正的RESTful**(或者说Roy Fielding博士提出的REST架构风格)核心是**资源**和**超媒体**。你的API应该返回资源的状态,而状态之间的转换应该通过服务端返回的链接(Hypermedia)来驱动。

听起来很美好,对吧?但现实是——99%的项目根本不需要这么复杂。

## 实际工作中的真相

让我告诉你一个残酷的事实:**大部分业务场景下,RPC风格的API比RESTful更好用。**

什么意思?看看这几个例子:

### 场景一:批量操作

```json
// 你想告诉前端:把用户A改成"活跃",用户B改成"禁用"
PATCH /users/batch
{
"updates": [
{"id": "A", "status": "active"},
{"id": "B", "status": "disabled"}
]
}
```

这有什么问题吗?一点问题都没有。但如果你坚持RESTful,你会陷入"到底是PATCH还是POST"的哲学讨论,最后写出一篇800字的RFC文档来解释为什么你的批量更新是合法的。

### 场景二:复杂的查询

```json
GET /orders?status=pending&user_id=123&created_at__gte=2024-01-01&sort=-created_at&page=2&page_size=20
```

这种查询参数,是RESTful允许的,没问题。但当你的查询条件变得复杂时,你会发现自己在 reinventing SQL。与其这样,不如坦然接受:

```json
POST /orders/search
{
"filters": {...},
"sort": [...],
"pagination": {...}
}
```

这不丢人,真的。

### 场景三:动作类操作

有些操作,它就是一个**动作**,不是资源状态的变化。

```json
POST /users/123/send-verification-code
POST /orders/456/cancel
POST /payments/789/refund
```

你说这不符合RESTful?我说你的业务逻辑它就是需要一个动作。强扭的瓜不甜,别为了所谓的"规范"折磨自己。

## 我的建议:实用主义至上

说了这么多,我的立场是:**不要为了RESTful而RESTful。**

那么,怎么设计一个好的API?以下是我的几点建议,踩过无数坑后的血泪总结:

### 1. URL是名词,不是动词

```bash
# ✅ 好
GET /users
POST /users
GET /users/123
DELETE /users/123

# ❌ 不好
GET /getUsers
POST /createUser
GET /deleteUser/123
```

这条基本准则还是值得遵守的,它让API有基本的可预测性。

### 2. 用对HTTP方法

| 方法 | 用途 |
|------|------|
| GET | 查询资源,不改变状态 |
| POST | 创建资源 |
| PUT | 完整替换资源 |
| PATCH | 部分更新资源 |
| DELETE | 删除资源 |

别偷懒,什么都用POST。你省下的每一行代码,都会在将来某一天变成维护者的眼泪。

### 3. 返回一致的结构

```json
{
"success": true,
"data": {...},
"error": {
"code": "USER_NOT_FOUND",
"message": "用户不存在"
}
}
```

无论成功还是失败,结构要统一。这会让你的前端同事少掉头发。

### 4. 版本号要果断

```bash
/api/v1/users
/api/v2/users
```

不要试图通过复杂的参数来兼容所有版本。版本号是最好的解耦方式。v1维护不动了?好,优雅地deprecated,然后砍掉。

### 5. 文档是最好的API设计

在写代码之前,先把API文档写出来。让你的调用方(前端、产品、第三方)确认:这确实是他们要的。

没有文档的API,就像没有说明书的电器——你敢用吗?

## 写在最后

回到最初的问题:RESTful重要吗?

**重要,但不是全部。**

RESTful是一个美好的愿景,它让API有一定的可预测性,让不同系统之间有可能实现真正的互操作。但在实际工作中,实用性永远是第一位的。

与其纠结 PUT 和 PATCH 的区别,不如想想:
- 我的API好懂吗?
- 我的调用方能顺利使用吗?
- 以后改需求了,这个API能扛得住吗?

这些问题的答案,比你是不是"真正的RESTful"重要一百倍。

所以,别装了,放过自己,也放过你的API。**做个实用的程序员,不丢人。**

---

*本文适合:所有被"API规范"折磨过的后端工程师,以及想知道为什么后端同事总在"重构API"的前端工程师。*

*不适宜:把HTTP方法当QQ表情用的朋友。*

相关文章

告别配置地狱!OpenClaw代部署服务,让你的AI工具分钟级上线
API设计里的那些坑,我全踩遍了
Go 调度器辣么聪明,怎么还是把你代码写慢了?
Redis分布式锁:踩坑无数后的血泪总结
日志打得好,排查问题快;日志打得烂,CTO也完蛋
告别配置地狱!OpenClaw代部署服务来了

发布评论