Express 中间件与 Router 完整解析
一、应用级全局中间件
app.use():注册应用级中间件。
路由模块(Router)、express.json()、(req, res, next) => {},在 Express 眼里都是中间件。
// 挂载 todo 路由模块 app.use("/api/todos", todoRouter);含义是:把todoRouter挂到/api/todos下;请求进来时先匹配路径前缀,再交给 router 里的路由。
二、app.use 怎么区分参数?
Express 的规则很简单:
app.use([path,] ...middleware)// 伪代码,表达 Express 的解析逻辑 app.use = function (...args) { let path = "/"; // 默认:匹配所有路径 let middlewares = args; if (typeof args[0] === "string" || isPathPattern(args[0])) { path = args[0]; // 第一个是路径 middlewares = args.slice(1); // 后面才是中间件 } else if (typeof args[0] === "function") { // 第一个是函数 → 没有写路径,用默认 "/" path = "/"; middlewares = args; } // 把 middlewares 注册到 path 上 };| 参数 | 是什么 |
|---|---|
| 字符串 / 正则 / 数组 | 挂载路径(mount path) |
| 函数 | 中间件(含 Router、错误处理中间件) |
Router 本质上也是函数,签名是:
(req, res, next) => void所以能传给app.use()。
三、常见用法
1. 只传一个函数 —— 全局中间件
app.use((req, res, next) => { console.log("请求来了"); next(); });所有请求都会经过,没有路径限制。
你index.js第 8 行就是这种。
2. 只传一个「内置中间件函数」
app.use(express.json()); app.use(express.urlencoded({ extended: true }));同样是全局中间件,express.json()返回的就是一个:
(req, res, next) => {}3. 路径 + 路由中间件
app.use("/api/todos", todoRouter);只有 URL 以/api/todos开头才会进入todoRouter。
4. 路径 + 多个中间件(可链式写)
app.use("/api", authMiddleware, logMiddleware, someRouter);5. 错误处理中间件(4 个参数)
app.use((error, req, res, next) => { res.status(500).json({ error: error.message }); });Express 靠参数个数是 4 个来识别错误中间件。
6. 只传 Router,不写路径
app.use(todoRouter); // 等价于挂载在 "/"一共多少种?
如果按「语法形式」数,常见就 6 类:
只传函数
只传内置中间件
路径 + Router
路径 + 多个中间件
错误处理中间件
只传 Router
如果按「本质」数,其实就 2 种:
普通中间件
错误处理中间件
Router、express.json()、404 兜底,都属于普通中间件,只是职责不同。
四、app.get 和 router.get 的区别
两者语法一样,差别在「注册在哪、路径怎么算」。
| app.get | router.get | |
|---|---|---|
| 注册对象 | 主应用 app | 子路由 router |
| 路径 | 完整路径,如/health | 相对路径,相对挂载点 |
| 匹配方法 | 只匹配 GET | 只匹配 GET |
| 典型用途 | 少量独立路由 | 一组相关路由拆到单独文件 |
小结
app.use没有「路由模块」这个特殊类型——Router 就是中间件,只是里面又注册了router.get/post/...。
区分参数:
第一个是路径(可选)
后面全是中间件函数(可多个)
app.getvsrouter.get:
app.get写绝对路径router.get写相对路径靠
app.use(prefix, router)拼成完整 URL
五、router.use 详解
router.use和app.use语法、解析规则完全一样,区别只是作用范围:
router.use([path,] ...middleware)第一个path同样是可选;
不传时默认/,但这里的/是相对于 Router 被挂载的位置。
六、四种常见用法
用法 1:Router 级全局中间件
const router = Router(); router.use((req, res, next) => { console.log("进入 todo 模块", req.path); next(); }); router.get("/", ...); router.get("/:id", ...);配合:
app.use("/api/todos", router);效果:
只有访问/api/todos/*时才会进这个中间件;
/health不会。
用法 2:Router 内带子路径
router.use("/archive", archiveRouter);若:
app.use("/api/todos", router);则完整路径是:
/api/todos/archive/*用法 3:某个子路径 + 中间件
router.use("/:id", validateId, loadTodo);只对:
/api/todos/123这类带:id的请求执行validateId和loadTodo,
/api/todos(列表)不会走。
用法 4:错误处理
router.use((err, req, res, next) => { res.status(500).json({ error: err.message }); });和app.use一样,4 个参数表示错误中间件,但只处理这个 router 链路上抛出的错误。
总结
app.use 本质
app.use([path,] ...middleware);path 可选
middleware 可多个
Router 本质也是中间件
Router 本质
const router = Router();Router 是一个中间件容器:
router.get(...) router.post(...) router.use(...)最后通过:
app.use("/prefix", router);挂载到主应用。
路径拼接规则
app.use("/api/todos", router); router.get("/");最终:
GET /api/todosapp.use("/api/todos", router); router.get("/:id");最终:
GET /api/todos/:id错误中间件识别规则
(err, req, res, next)只要参数个数为 4,Express 就会把它当成错误处理中间件。