Node.js + Express 入门实战笔记-02-中间件详解

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 类:

  1. 只传函数

  2. 只传内置中间件

  3. 路径 + Router

  4. 路径 + 多个中间件

  5. 错误处理中间件

  6. 只传 Router

如果按「本质」数,其实就 2 种:

  1. 普通中间件

  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.get vs router.get

  • app.get 写绝对路径

  • router.get 写相对路径

  • app.use(prefix, router) 拼成完整 URL


五、router.use 详解

router.useapp.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 的请求执行 validateIdloadTodo

复制代码
/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/todos

复制代码
app.use("/api/todos", router);

router.get("/:id");

最终:

复制代码
GET /api/todos/:id

错误中间件识别规则

复制代码
(err, req, res, next)

只要参数个数为 4,Express 就会把它当成错误处理中间件。