RESTful 接口实战:用户增删改查
🎯 引言
这篇是课程的综合实战。学完这篇文章,你能把前面学过的路由、Router、中间件、错误处理全部串起来,从零搭出一套规范的用户增删改查接口,还能解决前端请求时的跨域问题,让前后端真正联动起来。
🧱 什么是 RESTful
RESTful 是一种接口设计风格,核心思想是:用 URL 表示资源,用请求方法表示操作。以“用户”这个资源为例:
| 请求方法与地址 | 含义 |
|---|---|
GET /users | 获取用户列表 |
GET /users/1 | 获取 1 号用户详情 |
POST /users | 新增一个用户 |
PUT /users/1 | 更新 1 号用户 |
DELETE /users/1 | 删除 1 号用户 |
地址统一是 /users,操作语义全在请求方法上。接口一多,这种风格的好处就出来了:看一眼地址和方法,就知道这个接口是干什么的。
🛠 搭建项目骨架
在上一篇的项目基础上继续。先规划目录结构:
express-demo
├── app.js # 入口:挂载中间件和路由
├── routes
│ └── user.js # 用户模块的所有接口
└── package.json
用 Router 把用户接口拆到 routes/user.js,数据用一个内存数组模拟:
import { Router } from 'express';
const router = Router();
// 模拟数据库:内存数组
let users = [
{ id: 1, name: '小明', age: 18 },
{ id: 2, name: '小红', age: 20 },
];
export default router;
🛠 查询接口:列表与详情
两个 GET 接口:列表返回整个数组,详情用路径参数找指定用户,找不到就返回 404:
// GET /users:用户列表
router.get('/', (req, res) => {
res.json({ code: 0, data: users });
});
// GET /users/:id:用户详情
router.get('/:id', (req, res) => {
const id = Number(req.params.id);
const user = users.find((item) => item.id === id);
if (!user) {
return res.status(404).json({ code: 404, message: '用户不存在' });
}
res.json({ code: 0, data: user });
});
注意 req.params.id 是字符串,要用 Number() 转成数字再比较。
🛠 新增接口:POST
客户端把新用户数据放在请求体里提交,服务器解析后生成 id 加入数组:
// POST /users:新增用户
router.post('/', (req, res) => {
const { name, age } = req.body;
if (!name) {
return res.status(400).json({ code: 400, message: 'name 不能为空' });
}
const newUser = {
id: users.length + 1,
name,
age,
};
users.push(newUser);
// 创建成功,按 REST 规范返回 201
res.status(201).json({ code: 0, data: newUser });
});
🛠 更新与删除接口:PUT、DELETE
更新和删除都要先按 id 找到目标用户,找不到返回 404:
// PUT /users/:id:更新用户
router.put('/:id', (req, res) => {
const id = Number(req.params.id);
const user = users.find((item) => item.id === id);
if (!user) {
return res.status(404).json({ code: 404, message: '用户不存在' });
}
const { name, age } = req.body;
user.name = name ?? user.name;
user.age = age ?? user.age;
res.json({ code: 0, data: user });
});
// DELETE /users/:id:删除用户
router.delete('/:id', (req, res) => {
const id = Number(req.params.id);
const index = users.findIndex((item) => item.id === id);
if (index === -1) {
return res.status(404).json({ code: 404, message: '用户不存在' });
}
users.splice(index, 1);
res.json({ code: 0, message: '删除成功' });
});
🛠 入口文件:组装一切
入口 app.js 负责挂载中间件和路由,并配上 404 兜底和错误处理:
import express from 'express';
import userRouter from './routes/user.js';
const app = express();
// 解析 JSON 请求体
app.use(express.json());
// 挂载用户路由
app.use('/users', userRouter);
// 404 兜底
app.use((req, res) => {
res.status(404).json({ code: 404, message: '接口不存在' });
});
// 统一错误处理
app.use((err, req, res, next) => {
console.error('出错了:', err.message);
res.status(500).json({ code: 500, message: '服务器开小差了,请稍后再试' });
});
app.listen(3000, () => {
console.log('服务器已启动:http://localhost:3000');
});
用 node --watch app.js 启动,然后逐个验证:
# 用户列表
curl http://localhost:3000/users
# 新增用户
curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d "{\"name\":\"小华\",\"age\":22}"
# 更新用户
curl -X PUT http://localhost:3000/users/1 -H "Content-Type: application/json" -d "{\"age\":19}"
# 删除用户
curl -X DELETE http://localhost:3000/users/2
🪤 跨域问题与 cors
接口写好了,前端一请求却报错了。打开浏览器控制台,会看到类似 Access-Control-Allow-Origin 的报错,这就是跨域。
浏览器有同源策略:只有协议、域名、端口完全相同的地址才能互相请求。你的前端项目跑在 http://localhost:5173,接口在 http://localhost:3000,端口不同就算跨域,浏览器会把响应拦下来。
后端最常用的解决方案是加 CORS 响应头,告诉浏览器“我允许跨域访问”。手动设置响应头比较繁琐,直接用 cors 中间件(本课程使用 cors 2.8.6,配合 Node.js v22 和 Express 5.2.1 验证):
npm install cors
import express from 'express';
import cors from 'cors';
import userRouter from './routes/user.js';
const app = express();
// 允许跨域请求,放在路由之前
app.use(cors());
app.use(express.json());
app.use('/users', userRouter);
app.listen(3000);
一行 app.use(cors()) 就为所有接口加上了跨域响应头,前端再请求就不会被浏览器拦截了。
💡 前端调用示例
前端用 fetch 就能请求这套接口,写法和你平时调后端接口一样:
// 获取用户列表
const res = await fetch('http://localhost:3000/users');
const result = await res.json();
console.log(result.data); // 用户数组
// 新增用户
await fetch('http://localhost:3000/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: '小华', age: 22 }),
});
用 Axios 也一样,把 url 指向 http://localhost:3000/users 即可。至此,从写接口到前端调通的完整链路就打通了。
🧾 小节总结
- RESTful 风格用 URL 表示资源、请求方法表示操作,增删改查对应 GET、POST、PUT、DELETE。
- 用 Router 把一类接口拆到独立文件,入口文件只负责挂载中间件和路由。
- 内存数组可以模拟数据库练习接口结构,之后换成数据库操作即可。
- 同源策略导致前端跨端口请求被浏览器拦截,用
cors中间件一行代码解决。 - 404 兜底和错误处理中间件放在所有路由之后,保证任何意外都有统一响应。
❓ 知识问答
Q1:为什么刷新一下数据就没了?
数据存在内存数组里,进程重启就清空了。这是练习用的临时方案,真实项目要接数据库来持久化数据。
Q2:PUT 和 POST 有什么区别?
POST 用于新增资源,PUT 用于更新已有资源。PUT 的地址要带 id(如 /users/1),明确更新的是哪一条;POST 一般不带 id,由服务器生成。
Q3:cors() 不加参数是允许所有来源吗?
是的,app.use(cors()) 允许任何来源跨域访问,本地开发这样用很方便。生产环境建议配置 origin 白名单,只允许自己的前端域名访问。
Q4:新增用户时 id 用 users.length + 1 可靠吗?
练习场景够用,但删除中间一条数据后会产生重复 id。真实项目中 id 由数据库自增或使用 UUID 生成,不依赖数组长度。
🧪 小练习
仿照用户模块,实现一个图书模块 routes/book.js:包含 GET /books(列表)、GET /books/:id(详情)、POST /books(新增)三个接口,图书有 id 和 title 两个字段,并在 app.js 中挂载。
import { Router } from 'express';
const router = Router();
// 请在这里编写代码
export default router;
// 请在这里编写代码:挂载图书路由到 /books
🎉 恭喜你已经完成了 Express 课程的全部学习!从跑通第一个服务器,到路由、中间件、错误处理,再到亲手搭出一套 RESTful 接口,你已经具备了用 Node.js 独立开发后端接口的能力。接下来可以学习数据库(如 MySQL、MongoDB),把内存数组换成真正的持久化存储,继续加油!
