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.getrouter.get注册对象主应用 app子路由 router路径完整路径如/health相对路径相对挂载点匹配方法只匹配 GET只匹配 GET典型用途少量独立路由一组相关路由拆到单独文件小结app.use没有「路由模块」这个特殊类型——Router 就是中间件只是里面又注册了router.get/post/...。区分参数第一个是路径可选后面全是中间件函数可多个app.getvsrouter.getapp.get写绝对路径router.get写相对路径靠app.use(prefix, router)拼成完整 URL五、router.use 详解router.use和app.use语法、解析规则完全一样区别只是作用范围router.use([path,] ...middleware)第一个path同样是可选不传时默认/但这里的/是相对于 Router 被挂载的位置。六、四种常见用法用法 1Router 级全局中间件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不会。用法 2Router 内带子路径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)只要参数个数为 4Express 就会把它当成错误处理中间件。