express 洋葱模型
Express 的洋葱模型是对中间件(Middleware)执行流程的形象比喻,核心特点是:中间件按照“先进后出”的顺序执行,请求进入时依次经过中间件,响应返回时则反向经过中间件,类似洋葱从外层到内层再回到外层的穿透过程。
直观理解洋葱模型
假设我们有 3 个中间件 A
、B
、C
,按顺序注册到 Express 中:
javascript
const express = require('express');
const app = express();
// 中间件 A
app.use((req, res, next) => {
console.log('A 开始');
next(); // 调用 next() 进入下一个中间件
console.log('A 结束');
});
// 中间件 B
app.use((req, res, next) => {
console.log('B 开始');
next();
console.log('B 结束');
});
// 中间件 C(路由处理)
app.use((req, res) => {
console.log('C 处理请求');
res.send('Hello World');
console.log('C 结束');
});
app.listen(3000);
当有请求到达时,输出顺序为:
A 开始 → B 开始 → C 处理请求 → C 结束 → B 结束 → A 结束
这个流程就像洋葱:
- 请求进入时(从外到内):
A
→B
→C
- 响应返回时(从内到外):
C
→B
→A
核心原理
- 中间件的注册顺序即执行顺序:Express 按
app.use()
的调用顺序维护一个中间件队列,请求到达时依次执行。 next()
函数的作用:- 调用
next()
会暂停当前中间件,移交控制权给下一个中间件。 - 当后续中间件执行完毕(或没有更多中间件),会回溯到上一个中间件
next()
之后的代码继续执行。
- 调用
- 异步支持:如果中间件包含异步操作(如数据库查询),需在异步操作完成后调用
next()
,确保洋葱模型的顺序性:javascriptapp.use(async (req, res, next) => { console.log('A 开始'); await db.query(/* 异步操作 */); // 等待异步完成 next(); // 进入下一个中间件 console.log('A 结束'); });
为什么需要洋葱模型?
- 统一的请求/响应处理:例如,在最外层中间件处理日志记录(
A 开始
记录请求开始,A 结束
记录请求耗时)。 - 共享数据与流程控制:中间件可以在
req
或res
对象上挂载数据(如用户信息),后续中间件可读取,回溯时还能修改响应。 - 错误处理:错误处理中间件(带 4 个参数
(err, req, res, next)
)会捕获后续中间件抛出的错误,通过洋葱模型回溯到错误处理逻辑。
总结
Express 的洋葱模型本质是中间件的嵌套执行机制,通过 next()
函数实现“进入-回溯”的流程。这种设计让请求/响应的生命周期管理更灵活,便于实现日志、认证、错误处理等跨切面功能。