【从0开始学习计算机网络】| RESTful API 设计原则

🌈个人主页:一条泥憨鱼(欢迎各位大佬莅临)

🎬精选专栏传送门:

❄️《数据结构》 ❄️《AI与Agent那些事》

❄️《从0开始学计算机网络》 ❄️《后端开发》

前言:

一位前端从事者调接口,一会儿用 POST 拿数据,一会儿用 GET 删记录。URL 长这样:`/api/getUserInfo`、`/api/deleteUser?id=123`、`/api/userAction`。问后端这个接口是干嘛的,他说"你猜"。

结果就是前端天天问"这个接口返回啥格式",后端天天改接口不敢重构。联调两周,头发掉一半。

后来花了半天时间,把所有接口按照 RESTful 风格重新梳理了一遍。世界清静了。

这篇文章就聊聊 RESTful API 到底是怎么回事。

什么是 REST?

很多人一听 REST 就觉得是某种规范或者协议,其实不是。REST 是一种架构风格,更像是一种"约定俗成的默契"。

拿图书馆借书来类比。

假设图书馆有一套完整的图书管理系统。每一本书都是一个资源,比如《三体》这本书,它在系统里有一个唯一标识,可能是 `books/12345`。你想借书、还书、查书、预约,都是针对"书"这个资源做操作。

REST 的核心思想就一句话:把后端的数据和服务都看成资源,用 HTTP 方法表达对资源的操作。

复制代码
资源:books/12345(《三体》这本书)

操作:查 -> GET books/12345

     借 -> POST books/12345/borrow

     还 -> PUT books/12345/return

你发现没有,URL 里全是名词,操作全在 HTTP 方法里。这就是 REST 和那种"URL 里写动词"(`/getBook`、`/deleteBook`)的根本区别。

REST 不是标准,没有 RFC 文档说"你必须这么做"。它是一种风格,大家约定俗成。好处是:只要大家都守规矩,接口的可读性和可维护性会好很多。

资源命名:URL 设计的黄金法则

资源命名是 RESTful 设计里最直观、也最容易做错的部分。几条黄金法则,记牢就行。

第一,用名词复数,不用动词。

✅ GET /users 获取用户列表

✅ GET /users/123 获取某个用户

✅ POST /users 创建用户

❌ GET /getUsers

❌ POST /createUser

第二,层级关系用斜杠表达。

✅ GET /users/123/orders 用户 123 的订单列表

✅ GET /users/123/orders/456 用户 123 的订单 456

❌ GET /getUserOrders?userId=123

第三,不用大写,用连字符。

URL 是区分大小写的,为了避免混乱,统一小写。多个单词用 `-` 连接,不要用下划线。

✅ GET /user-profiles

❌ GET /userProfiles

❌ GET /user_profiles

第四,不要暴露内部实现细节。

比如你的表叫 `t_user_info`,URL 别跟着叫 `/t_user_info`。API 是对外暴露的契约,应该用业务语言,不是技术语言。

这里有个小技巧:设计 URL 时,想象你是在浏览一个文件系统。`/users/123/orders/456` 就像路径 `users/123/orders/456`,一层一层往下走,清晰自然。

HTTP 方法怎么选:GET/POST/PUT/PATCH/DELETE

五个常用方法,每个都有明确语义。选对了,接口意图一目了然。

| 方法 | 用途 | 是否幂等 |

| GET | 查询资源 | ✅ 幂等 |

| POST | 创建资源 | ❌ 不幂等 |

| PUT | 整体更新资源 | ✅ 幂等 |

| PATCH | 局部更新资源 | ❌ 不幂等 |

| DELETE | 删除资源 | ✅ 幂等 |

  • GET 请求一百次,数据不会变。

  • PUT 把用户名字改成"张三",执行十次,结果还是"张三"。

  • DELETE 删除一个用户,删第一次成功了,再删就返回 404,但资源状态没变(还是不存在)。

  • 但 POST 创建用户,执行十次就创建十个用户。PATCH 每次执行可能基于当前状态做修改,也可能不幂等。

这个特性在重试机制 里特别重要。比如网络超时,客户端不确定请求是否成功,就会重试。如果用的是 POST,重试可能导致数据重复创建。所以很多系统会引入**幂等键(Idempotency-Key)**来解决这个问题,但那是后话了。

看一个 Node.js + Express 的伪代码示例:

javascript 复制代码
// 用户资源的路由设计

const express = require('express');

const router = express.Router();



// GET /users --- 获取用户列表

router.get('/users', (req, res) => {

  // 从数据库查询用户列表

  const users = db.findMany('users');

  res.json({ data: users });

});



// GET /users/:id --- 获取单个用户

router.get('/users/:id', (req, res) => {

  const user = db.findOne('users', req.params.id);

  if (!user) {

    return res.status(404).json({ error: '用户不存在' });

  }

  res.json({ data: user });

});



// POST /users --- 创建用户

router.post('/users', (req, res) => {

  const newUser = db.insert('users', req.body);

  // 201 = Created,带上新资源的完整信息

  res.status(201).json({ data: newUser });

});



// PUT /users/:id --- 整体更新(客户端传完整对象)

router.put('/users/:id', (req, res) => {

  const updated = db.replace('users', req.params.id, req.body);

  res.json({ data: updated });

});



// PATCH /users/:id --- 局部更新(只传要改的字段)

router.patch('/users/:id', (req, res) => {

  const patched = db.update('users', req.params.id, req.body);

  res.json({ data: patched });

});



// DELETE /users/:id --- 删除用户

router.delete('/users/:id', (req, res) => {

  db.remove('users', req.params.id);

  // 204 = No Content,删除成功不返回 body

  res.status(204).send();

});

注意几个细节:

  • POST 创建成功后返回 201,不是 200。

  • DELETE 成功后返回 204,不带 body。

  • PUT 和 PATCH 的区别:PUT 是"整体替换",客户端要传完整的资源对象;PATCH 是"局部修改",只传要改的字段。

状态码与错误处理:别只返回 200

很多新手写接口,不管成功失败都返回 200,然后在 body 里塞一个 `{ code: 500, message: "出错了" }`。

这个做法很坑。

HTTP 状态码本身就是协议的一部分,它有明确的语义。你返回 200,客户端就以为成功了,然后才发现 body 里有个 error,还得自己解析。这等于把 HTTP 协议废掉,自己发明了一套协议

用正确的状态码,客户端可以直接根据状态码判断结果,省掉很多无谓的解析逻辑。

常用状态码速查:

| 状态码 | 含义 | 典型场景 |

| 200 | 成功 | GET/PUT/PATCH 成功 |

| 201 | 创建成功 | POST 创建资源 |

| 204 | 无内容 | DELETE 成功 |

| 400 | 请求参数错误 | 必填字段缺失、格式不对 |

| 401 | 未认证 | 没登录或 token 过期 |

| 403 | 无权限 | 登录了但没权限操作 |

| 404 | 资源不存在 | URL 写错或资源被删 |

| 409 | 冲突 | 创建重复资源、状态冲突 |

| 422 | 语义错误 | 请求格式对但业务上不合法 |

| 500 | 服务器内部错误 | 代码报错、数据库挂了 |

踩坑点:状态码用对了,但错误响应的格式五花八门。

有的接口返回 `{ error: "xxx" }`,有的返回 `{ message: "xxx" }`,有的返回 `{ msg: "xxx" }`。前端同事每次接新接口都要看文档才知道怎么取错误信息。

统一错误响应结构,约定一个格式:

javascript 复制代码
{

  "error": {

    "code": "USER_NOT_FOUND",

    "message": "用户不存在",

    "details": "可选,补充说明"

  }

}

或者更简单的:

javascript 复制代码
{

  "code": "USER_NOT_FOUND",

  "message": "用户不存在"

}

关键是全局统一。不管哪个接口报错,格式都一样。前端可以写一个统一的错误拦截器,不用每个接口单独处理。

还有一点:业务错误码和 HTTP 状态码的关系。HTTP 状态码管"请求是否成功",业务错误码管"具体什么原因失败"。两者配合使用,不要混为一谈。

版本管理与过滤排序分页

接口上线后,需求总会变。用户字段要加、接口逻辑要改。但老版本的客户端还在用,不能直接改掉。所以版本管理很重要。

两种主流方式:

方式一:URL 路径版本号

javascript 复制代码
GET /api/v1/users

GET /api/v2/users```

简单直观,容易路由,是目前最常用的方式。缺点是 URL 不够干净,但换来的是明确和好维护。

方式二:Header 版本号

javascript 复制代码
GET /api/users

Accept: application/vnd.myapp.v2+json

URL 干净,但调试起来麻烦,而且对客户端要求高。适合对 API 纯净度有执念的团队。

对小白来说,无脑选 URL 路径版本号就行。等以后有需求了,再考虑 Header 方案。

过滤、排序、分页也是高频需求。REST 风格下,这些都用**查询参数**实现。

javascript 复制代码
GET /api/v1/users?status=active&age=18&sort=-created_at&page=2&page_size=20

- `status=active` --- 过滤条件,多个条件用 `&` 连接

- `age=18` --- 精确过滤

- `sort=-created_at` --- 按创建时间倒序,`-` 表示倒序,没有 `-` 是正序

- `page=2&page_size=20` --- 第 2 页,每页 20 条

分页响应也要有约定:

javascript 复制代码
{

  "data": [...],

  "pagination": {

    "page": 2,

    "page_size": 20,

    "total": 156,

    "total_pages": 8

  }

}

把分页信息放在 `pagination` 字段里,前端处理起来就很方便。

踩坑点:

过滤参数不要搞得太花哨。比如 `filter=status:active,age:18` 这种 DSL 风格,看着高级,实际难维护。老老实实用 `?status=active&age=18` 就挺好。

总结:一张自查清单

RESTful API 设计的核心就四个字:面向资源 。一切围绕"资源"展开,URL 表达资源位置,HTTP 方法表达操作意图,状态码表达结果。

最后给一张自查清单,写完接口过一遍:

  • URL 用的是名词复数,没有动词?

  • 层级关系用 `/` 表达,而不是查询参数?

  • 没有大写字母,多词用 `-` 连接?

  • GET 只做查询,POST 只做创建?

  • PUT 整体更新,PATCH 局部更新?

  • 创建返回 201,删除返回 204?

  • 错误响应格式全局统一?

  • 接口有版本号?

  • 列表接口支持过滤、排序、分页?

这些规则不复杂,但真要做好,需要团队统一认知。建议把这份清单贴到团队文档里,下次评审接口设计时逐条过。

REST 不是终点。现在 GraphQL、gRPC 也都很流行,各有各的适用场景。但 REST 作为最基础、最通用的 API 设计风格,值得每个后端开发者掌握扎实。地基打牢了,学什么都快。

相关推荐
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第210题】【10_网络协议篇】第1题:说说 TCP/IP 网络五层模型
java·网络协议·tcp/ip·计算机网络·网络五层模型
不在逃避q3 小时前
使用.NET实现自带思考的Tool 并且提供mcp streamable http服务
网络协议·http·.net
一条泥憨鱼4 小时前
【从0开始学习计算机网络】| HTTP方法疑点解析
开发语言·计算机网络·http
外滩运维专家12 小时前
HTTPS 证书报错排查手册:6 个高频错误码及解决方法
网络协议·http·https
纵有疾風起14 小时前
计算机网络的性能指标体系:带宽、时延、吞吐量
计算机网络·rtt·408·带宽·性能指标·吞吐量·时延
鲜花飘飘扬16 小时前
HTTP请求头中表示代理IP地址的属性及获取情况
网络协议·tcp/ip·http
纵有疾風起1 天前
从OSI到TCP/IP——分层架构的思想根源与模型之争
tcp/ip·计算机网络·架构·osi·408·体系结构·分层
实心儿儿1 天前
Linux —— 应用层协议HTTP
linux·网络·http
游戏开发爱好者81 天前
TraceEagle 代理抓包详解,无感代理、去证书绑定与 HTTP/3 解密能力解析
网络协议·计算机网络·网络安全·ios·adb·https·udp