Skip to content

Node.js RESTful API

REST 是 REpresentational State Transfer(表述性状态转移)的缩写。它是一种架构风格,而不是一种特定的技术或协议,用于设计网络应用程序。遵循 REST 原则的系统(即 RESTful 系统)通常使用 HTTP 协议进行通信。

REST 的关键原则包括:

  • 客户端-服务器架构 (Client-Server Architecture):将客户端(请求数据)与服务器(管理数据)的关注点分离。
  • 无状态 (Statelessness):客户端到服务器的每个请求都必须包含理解和处理该请求所需的所有信息。服务器在请求之间不存储任何客户端上下文。
  • 可缓存 (Cacheability):响应必须定义自身是可缓存还是不可缓存,以提高性能。
  • 分层系统 (Layered System):客户端通常无法判断是直接连接到最终服务器,还是连接到中间层。
  • 统一接口 (Uniform Interface):通用接口简化并解耦了架构。这通常包括:
  • 资源标识 (Resource Identification):资源(例如,用户、产品)使用 URI(Uniform Resource Identifiers,统一资源标识符)标识,例如 /users/123。
  • 通过表述操作资源 (Resource Manipulation Through Representations):客户端通过资源的表述(通常是 JSON 或 XML)与资源交互。
  • 自描述消息 (Self-descriptive Messages):每条消息包含足够的信息来描述如何处理它(例如,使用 HTTP 方法和诸如 Content-Type 的头部)。
  • 作为应用状态引擎的超媒体 (Hypermedia as the Engine of Application State (HATEOAS)):响应可以包含指向相关操作或资源的链接,允许客户端动态导航 API(实践中较少完全实现)。

RESTful API 利用标准的 HTTP 方法(动词)来对资源执行操作:

HTTP 方法操作示例 URI描述
GET读取/users检索用户列表。
GET读取/users/123检索 ID 为 123 的特定用户。
POST创建/users创建新用户(数据在请求体中发送)。
PUT更新/替换/users/123更新/替换 ID 为 123 的用户(整个资源数据在请求体中发送)。
PATCH部分更新/users/123部分更新 ID 为 123 的用户(仅将更改的字段在请求体中发送)。
DELETE删除/users/123删除 ID 为 123 的用户。

根据 REST 原则设计的 Web 服务被称为 RESTful Web 服务或 RESTful API。它们提供了一种标准化的方式,供不同的应用程序(例如,Web 前端、移动应用程序、另一个后端服务)进行通信和交换数据,通常使用 HTTP 和 JSON。

使用 Express 创建一个简单的 RESTful API

Section titled “使用 Express 创建一个简单的 RESTful API”

我们将使用流行的 Express.js 框架来构建一个简单的 API,用于管理用户列表。为简单起见,我们将用户数据存储在一个 JSON 文件(users.json)中,充当一个基本的数据库。

为你的项目创建一个新目录,进入该目录,并初始化 npm:

mkdir simple-rest-api
cd simple-rest-api
npm init -y

安装 Express:

npm install express

创建一个名为 users.json 的文件,并添加一些初始数据:

{
"1": {
"name": "Alice",
"profession": "Engineer",
"id": 1
},
"2": {
"name": "Bob",
"profession": "Designer",
"id": 2
},
"3": {
"name": "Charlie",
"profession": "Manager",
"id": 3
}
}

创建一个名为 server.js 的文件,并设置基本的 Express 服务器:

const express = require('express');
const fs = require('fs').promises; // 使用 promises API 进行异步操作
const path = require('path');
const app = express();
const port = 3000;
const usersFilePath = path.join(__dirname, 'users.json');
// 用于解析 JSON 请求体的中间件
app.use(express.json());
// --- API 端点 ---
// 用于读取用户数据的辅助函数
async function getUsersData() {
try {
const data = await fs.readFile(usersFilePath, 'utf8');
return JSON.parse(data);
} catch (error) {
// 如果文件不存在或发生其他读取错误,返回空对象
if (error.code === 'ENOENT') {
return {};
}
console.error("Error reading users file:", error);
throw error; // 重新抛出其他错误
}
}
// 用于写入用户数据的辅助函数
async function writeUsersData(data) {
try {
await fs.writeFile(usersFilePath, JSON.stringify(data, null, 2), 'utf8');
} catch (error) {
console.error("Error writing users file:", error);
throw error;
}
}
// GET /api/users - 列出所有用户
app.get('/api/users', async (req, res) => {
try {
const users = await getUsersData();
// 返回用户数组,而不是以 ID 为键的对象
res.json(Object.values(users));
} catch (error) {
res.status(500).send('Error retrieving user data');
}
});
// GET /api/users/:id - 根据 ID 获取单个用户
app.get('/api/users/:id', async (req, res) => {
const userId = req.params.id;
try {
const users = await getUsersData();
const user = users[userId];
if (user) {
res.json(user);
} else {
res.status(404).send('User not found');
}
} catch (error) {
res.status(500).send('Error retrieving user data');
}
});
// POST /api/users - 添加新用户
app.post('/api/users', async (req, res) => {
const newUserInfo = req.body;
if (!newUserInfo || !newUserInfo.name || !newUserInfo.profession) {
return res.status(400).send('Missing user name or profession in request body');
}
try {
const users = await getUsersData();
const existingIds = Object.keys(users).map(id => parseInt(id, 10));
const newId = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 1;
const newUser = {
...newUserInfo, // 扩展运算符(...)用于复制属性
id: newId
};
users[newId] = newUser;
await writeUsersData(users);
res.status(201).json(newUser); // 201 Created 状态码
} catch (error) {
res.status(500).send('Error adding user');
}
});
// DELETE /api/users/:id - 删除用户
app.delete('/api/users/:id', async (req, res) => {
const userId = req.params.id;
try {
const users = await getUsersData();
if (users[userId]) {
delete users[userId];
await writeUsersData(users);
res.status(204).send(); // 204 No Content 状态码(成功,无响应体)
} else {
res.status(404).send('User not found');
}
} catch (error) {
res.status(500).send('Error deleting user');
}
});
// PUT /api/users/:id - 更新/替换用户(示例)
app.put('/api/users/:id', async (req, res) => {
const userId = req.params.id;
const updatedUserInfo = req.body;
if (!updatedUserInfo || !updatedUserInfo.name || !updatedUserInfo.profession) {
return res.status(400).send('Missing user name or profession in request body for update');
}
try {
let users = await getUsersData();
if (!users[userId]) {
return res.status(404).send('User not found');
}
// 完全替换用户信息
users[userId] = {
...updatedUserInfo,
id: parseInt(userId, 10) // 确保 ID 类型正确
};
await writeUsersData(users);
res.json(users[userId]);
} catch (error) {
res.status(500).send('Error updating user');
}
});
// 启动服务器
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});

在项目目录中打开终端并运行:

$ node server.js

你应该看到:Server listening at http://localhost:3000

你可以使用终端中的 curl 工具、Postman、Insomnia,甚至你的 Web 浏览器(用于 GET 请求)与你的 API 进行交互:

  • 列出用户 (GET): 在浏览器中打开 http://localhost:3000/api/users 或使用 curl http://localhost:3000/api/users
  • 获取用户 2 (GET): 打开 http://localhost:3000/api/users/2 或使用 curl http://localhost:3000/api/users/2
  • 添加用户 (POST): 使用 curl -X POST -H "Content-Type: application/json" -d '{"name":"David", "profession":"Tester"}' http://localhost:3000/api/users
  • 删除用户 1 (DELETE): 使用 curl -X DELETE http://localhost:3000/api/users/1
  • 更新用户 3 (PUT): 使用 curl -X PUT -H "Content-Type: application/json" -d '{"name":"Charlie Jr.", "profession":"Senior Manager"}' http://localhost:3000/api/users/3

在使用 POST、PUT 或 DELETE 进行更改后,你可以再次使用 GET 请求来查看 users.json 中更新后的列表。

本示例演示了使用 Express 在 Node.js 中创建 RESTful API 的基础知识。实际应用通常会涉及更健壮的错误处理、输入验证、身份验证、授权,以及使用适当的数据库而非 JSON 文件。