Express 简介

RESTful 接口实战:用户增删改查

综合运用路由、中间件、错误处理知识,实现一套完整的用户增删改查 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,数据用一个内存数组模拟:

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:

routes/user.js
// 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 加入数组:

routes/user.js
// 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:

routes/user.js
// 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 兜底和错误处理:

app.js
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,端口不同就算跨域,浏览器会把响应拦下来。

同源策略是浏览器为了保护用户设的安全限制,服务器本身是正常返回了数据的。所以用 curl 请求没问题,浏览器里却报错。

后端最常用的解决方案是加 CORS 响应头,告诉浏览器“我允许跨域访问”。手动设置响应头比较繁琐,直接用 cors 中间件(本课程使用 cors 2.8.6,配合 Node.js v22 和 Express 5.2.1 验证):

npm install cors
app.js
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 就能请求这套接口,写法和你平时调后端接口一样:

api.js
// 获取用户列表
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(新增)三个接口,图书有 idtitle 两个字段,并在 app.js 中挂载。

routes/book.js
import { Router } from 'express';

const router = Router();

// 请在这里编写代码

export default router;
app.js
// 请在这里编写代码:挂载图书路由到 /books

🎉 恭喜你已经完成了 Express 课程的全部学习!从跑通第一个服务器,到路由、中间件、错误处理,再到亲手搭出一套 RESTful 接口,你已经具备了用 Node.js 独立开发后端接口的能力。接下来可以学习数据库(如 MySQL、MongoDB),把内存数组换成真正的持久化存储,继续加油!