REST(Representational State Transfer)是一种架构风格,用于设计网络应用程序的 API。遵循 REST 原则设计的 API 被称为 RESTful API。它利用 HTTP 协议的特性,提供简洁、可扩展、易于理解的接口。本章将深入讲解 RESTful API 的核心概念、设计规范和实践,并通过一个 Express 示例展示如何构建符合 REST 风格的 API。
REST 是由 Roy Fielding 在其博士论文中提出的一种架构风格,它不是标准,而是一组约束和原则。如果一个 API 符合 REST 的约束条件,则可称为 RESTful API。其核心思想是:
RESTful API 使用标准的 HTTP 方法对资源进行操作,对应关系如下:
| HTTP 方法 | CRUD 操作 | 作用范围 | 示例 |
|---|---|---|---|
| GET | Read(读取) | 单个资源或集合 | GET /users – 获取用户列表;GET /users/1 – 获取用户1 |
| POST | Create(创建) | 通常作用于集合 | POST /users – 创建新用户 |
| PUT | Update/Replace(全量更新) | 单个资源 | PUT /users/1 – 替换用户1的全部信息 |
| PATCH | Partial Update(部分更新) | 单个资源 | PATCH /users/1 – 更新用户1的部分字段 |
| DELETE | Delete(删除) | 单个资源或集合 | DELETE /users/1 – 删除用户1 |
注意:PUT 是幂等的,多次调用结果相同;POST 非幂等,每次调用可能创建新资源。PATCH 可以幂等也可以不幂等,取决于实现。
/users 而不是 /getUsers。/users/1/posts 表示用户1的所有文章,/posts/2/comments 表示文章2的评论。GET /users?role=admin&page=2&limit=20&sort=createdAt_desc。/users/1/activate 可以使用动词,但更符合 REST 的做法是使用 PATCH /users/1 传递 { "status": "active" }。使用合适的 HTTP 状态码可以让客户端更容易理解请求结果。常见分类:
200 OK:GET、PUT、PATCH 成功。201 Created:POST 创建成功,通常在响应头中返回 Location 指向新资源。204 No Content:DELETE 成功,或 PUT/PATCH 成功但无需返回内容。400 Bad Request:请求参数错误(如缺少必要字段、格式错误)。401 Unauthorized:未提供认证信息或认证失败。403 Forbidden:认证成功但无权限访问该资源。404 Not Found:资源不存在。405 Method Not Allowed:请求方法不允许。409 Conflict:请求与服务器当前状态冲突(如资源已存在)。422 Unprocessable Entity:请求语义错误(如验证失败)。500 Internal Server Error:服务器内部错误。502 Bad Gateway、503 Service Unavailable 等。通常使用 JSON 作为数据交换格式。建议统一响应结构,便于客户端处理。
例如,成功时返回:
{
"data": { ... },
"message": "操作成功"
}
失败时返回:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "邮箱格式不正确"
}
}
也可遵循 JSON:API 或 GraphQL 等规范,但简单项目中保持一致性即可。
随着 API 演化,可能需要引入版本控制。常见做法:
/api/v1/users、/api/v2/users。简单直观,最常用。Accept: application/vnd.myapi.v1+json。v1.api.example.com。推荐使用 URL 路径方式,因为它更显式,易于缓存和调试。
对于资源集合,通常需要支持这些功能。通过查询参数实现:
?field=value,例如 ?category=tech。复杂过滤可使用类似 ?filter[field]=value 或 ?field=value 均可。?sort=field 或 ?sort=-field(降序)。?page=2&limit=20 或 ?offset=20&limit=20。响应中应包含分页元数据,如总条数、当前页等。// 示例:分页响应
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 100,
"pages": 5
}
}
RESTful API 通常是无状态的,因此不能依赖 cookie-session。常用认证方式:
Authorization: Bearer <token>。JWT 自包含用户信息,适合分布式系统。授权通常在业务逻辑中实现,根据角色或权限限制对资源的访问。
良好的文档是 API 可用性的关键。推荐使用 Swagger / OpenAPI 规范,自动生成交互式文档。也可以使用 apidoc、Postman 等工具。
下面是一个基于 Express 和内存数组的示例,展示 RESTful 风格的用户管理 API。
const express = require('express');
const app = express();
app.use(express.json());
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
// 获取所有用户(支持过滤、分页)
app.get('/api/v1/users', (req, res) => {
let result = users;
// 过滤
if (req.query.name) {
result = result.filter(u => u.name.includes(req.query.name));
}
// 分页
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const start = (page - 1) * limit;
const end = start + limit;
const paginated = result.slice(start, end);
res.json({
data: paginated,
pagination: {
page,
limit,
total: result.length,
pages: Math.ceil(result.length / limit)
}
});
});
// 获取单个用户
app.get('/api/v1/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: '用户不存在' });
}
res.json({ data: user });
});
// 创建用户
app.post('/api/v1/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: '姓名和邮箱必填' });
}
const newUser = {
id: users.length + 1,
name,
email
};
users.push(newUser);
res.status(201).location(`/api/v1/users/${newUser.id}`).json({ data: newUser });
});
// 全量更新用户
app.put('/api/v1/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: '用户不存在' });
}
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: '姓名和邮箱必填' });
}
user.name = name;
user.email = email;
res.json({ data: user });
});
// 部分更新用户
app.patch('/api/v1/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: '用户不存在' });
}
if (req.body.name) user.name = req.body.name;
if (req.body.email) user.email = req.body.email;
res.json({ data: user });
});
// 删除用户
app.delete('/api/v1/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: '用户不存在' });
}
users.splice(index, 1);
res.status(204).send();
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: '服务器内部错误' });
});
app.listen(3000, () => console.log('API running on http://localhost:3000'));
这个示例演示了如何设计资源端点、使用适当的状态码、支持过滤和分页,以及错误处理。