Express 简介

Express 路由基础

掌握 Express 的请求方法路由、路径参数、查询参数,学会用 express.Router 模块化拆分路由。

🎯 引言

学完这篇文章,你能为服务器定义 GET、POST、PUT、DELETE 各种请求方法的路由,会读取路径参数和查询参数,还能用 express.Router 把路由拆分到独立文件,让项目结构更清晰。


🧱 什么是路由

路由就是“地址 + 请求方法”到“处理函数”的映射规则。 客户端请求 /users,服务器该执行哪段代码,就是路由说了算。

就像快递分拣中心:包裹(请求)到了,根据地址标签(URL)和类型(请求方法),分到不同的货架(处理函数)去。


🛠 四种常用请求方法

REST 风格的接口里,不同的请求方法对应不同的操作语义,Express 为每种方法提供了对应的注册函数:

app.js
import express from 'express';

const app = express();

app.get('/users', (req, res) => {
    res.send('获取用户列表');
});

app.post('/users', (req, res) => {
    res.send('新增一个用户');
});

app.put('/users', (req, res) => {
    res.send('更新用户信息');
});

app.delete('/users', (req, res) => {
    res.send('删除一个用户');
});

app.listen(3000);

地址相同、方法不同,会被路由到不同的处理函数,它们各司其职:

  • GET:查询数据,浏览器地址栏直接访问就是 GET 请求。
  • POST:新增数据,通常携带请求体。
  • PUT:更新数据。
  • DELETE:删除数据。
浏览器地址栏只能发起 GET 请求,想调试 POST、PUT、DELETE,可以在终端用 curl,或者借助接口调试工具,后面的文章会专门讲。

🛠 路径参数 req.params

接口经常需要根据 ID 操作某条数据,比如 /users/1 表示 1 号用户。ID 是变化的,不能为每个用户写一条路由,这时用路径参数

app.js
app.get('/users/:id', (req, res) => {
    res.send(`用户详情:${req.params.id}`);
});

路径里的 :id 是占位符,访问 /users/1/users/99 都能匹配这条路由,实际的值通过 req.params.id 拿到。

req.params 拿到的值永远是字符串。访问 /users/1req.params.id'1' 而不是数字 1,需要数字计算时记得用 Number() 转换。

🛠 查询参数 req.query

URL 问号后面的键值对叫查询参数,常用于分页、筛选,比如 /users?page=2&size=10。Express 自动解析好放在 req.query 里:

app.js
app.get('/users', (req, res) => {
    const { page, size } = req.query;

    res.send(`${page} 页,每页 ${size}`);
});

访问 http://localhost:3000/users?page=2&size=10,就会返回“第 2 页,每页 10 条”。

在 Express 5 中,req.query 是一个只读的 getter 属性,读取没问题,但不要尝试给它重新赋值。

⚡ 路径参数与查询参数怎么选

两者都能从 URL 里带数据,使用场景不同:

  • 路径参数:定位某一条具体资源,是 URL 结构的一部分,如 /users/1
  • 查询参数:附加的筛选条件,可有可无,如 ?page=2&keyword=张

记住一个判断标准:少了它接口就不知道操作谁,用路径参数;少了它接口依然能用默认值工作,用查询参数。


🛠 用 express.Router 拆分路由

所有路由都堆在 app.js 里,接口一多就没法维护。Router 是一个迷你版的路由对象,可以把同一类路由拆到独立文件:

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

const router = Router();

// 这里的 / 实际对应挂载路径 /users
router.get('/', (req, res) => {
    res.send('用户列表');
});

router.get('/:id', (req, res) => {
    res.send(`用户详情:${req.params.id}`);
});

export default router;

然后在入口文件里挂载:

app.js
import express from 'express';
import userRouter from './routes/user.js';

const app = express();

// 以 /users 开头请求交给 userRouter 处理
app.use('/users', userRouter);

app.listen(3000);

这样访问 /users/users/1 都会进入 routes/user.js。以后再加文章路由,就新建 routes/article.js 挂载到 /articles,每个文件只管自己的事。

ESM 导入本地文件必须写完整后缀 .js,写 import userRouter from './routes/user' 会报找不到模块,这一点上一个 Node.js 课程强调过。

💡 all 与 use 的区别

除了按方法注册,还有两个不太常用的写法,了解即可:

  • app.all('/users', handler):不管 GET、POST 还是其他方法,统统走这个处理函数。
  • app.use('/users', handler):匹配以 /users 开头的所有路径、所有方法,常用于挂载 Router 和中间件。

all 要求路径精确匹配,use 是前缀匹配,这是两者关键的区别。日常开发里按具体方法写 app.getapp.post 才是主流。


🧾 小节总结

  • 路由是“地址 + 请求方法”到处理函数的映射,Express 用 app.getapp.postapp.putapp.delete 注册。
  • 路径参数用 :id 占位,通过 req.params 读取,拿到的值是字符串。
  • 查询参数是问号后的键值对,通过 req.query 读取,Express 5 中它是只读的。
  • express.Router 可以把一类路由拆到独立文件,再用 app.use('/users', router) 挂载。
  • app.all 匹配所有方法且路径精确,app.use 匹配所有方法且按前缀匹配。

❓ 知识问答

Q1:路径参数和查询参数能同时用吗?

可以。比如 /users/1/comments?page=2req.params.id 拿到用户 ID,req.query.page 拿到页码,互不干扰。

Q2:/users/:id/users/new 两条路由都写了,访问 /users/new 走哪条?

Express 按注册顺序匹配,先注册的先命中。想让 /users/new 生效,要把它写在 /users/:id 前面,否则 new 会被当成 id

Q3:Router 拆出去后,路径要怎么写?

Router 文件里写相对路径。挂载时指定了 /users,Router 里的 / 就是 /users/:id 就是 /users/:id,不要在 Router 里再写一遍完整路径。

Q4:GET 请求能带请求体吗?

规范上 GET 没有请求体,要传数据就用路径参数或查询参数。需要提交数据时,请使用 POST、PUT 等方法。


🧪 小练习

为图书模块写路由并拆分到独立文件:

  1. 新建 routes/book.js,定义两条路由:GET /books 返回“图书列表”,GET /books/:id 返回“图书详情:id 值”。
  2. app.js 中把它挂载到 /books 路径下。
routes/book.js
// 请在这里编写代码
app.js
import express from 'express';

const app = express();

// 请在这里编写代码

app.listen(3000);

🎉 恭喜你已经掌握 Express 路由的常用写法!下一篇我们学习 Express 的灵魂:中间件机制。