本文目录导读:

- 核心原则:“Next” 函数
- Express.js 风格(顺序执行,基于回调)
- Koa.js 风格(洋葱模型,基于 async/await)
- 自定义 Pipeline(手写控制)
- 特殊场景:并行/条件性执行
- 总结:如何控制顺序?
通过函数的调用链和参数传递机制,显式或隐式地决定下一个中间件何时执行。
不同的中间件架构(如 Express/Koa/自定义Pipeline)控制方式略有不同,但原理相通,下面从最常见的场景和底层原理来详细说明。
核心原则:“Next” 函数
几乎所有的中间件系统(如 Express、Koa、Redux、Webpack Loader)都使用一个 next 函数来控制流程。
- 调用
next():表示“当前中间件处理完毕,将控制权交给下一个中间件”。 - 不调用
next():表示“请求到此为止”,后续中间件不会执行。 - 调用
next(error):表示“发生错误,跳转到错误处理中间件”。 - 调用
next('route')(某些框架特有):表示“跳过当前路由的剩余中间件,直接匹配下一个路由”。
Express.js 风格(顺序执行,基于回调)
在 Express 中,中间件按它们在代码中出现的顺序被添加到请求处理链中。
const express = require('express');
const app = express();
// 中间件 1:日志记录(先执行)
app.use((req, res, next) => {
console.log('1. 日志记录中间件');
// 关键:必须调用 next(),否则卡住
next();
});
// 中间件 2:请求解析(后执行)
app.use((req, res, next) => {
console.log('2. 解析请求体');
req.customData = { timestamp: Date.now() };
next();
});
// 中间件 3:路由处理
app.get('/data', (req, res) => {
console.log('3. 处理路由');
res.send(`Timestamp: ${req.customData.timestamp}`);
});
// 错误处理中间件(特殊,4个参数)
app.use((err, req, res, next) => {
console.error('错误中间件:', err.message);
res.status(500).send('服务器错误');
});
app.listen(3000);
控制要点:
- 代码顺序决定优先级:
app.use(middleware1)在前,middleware1先执行。 next()位置决定流程:可以在next()之前做前置处理(如日志),在next()之后做后置处理(如统一添加响应头)。
Koa.js 风格(洋葱模型,基于 async/await)
Koa 使用 async 函数和 await next() 实现“洋葱模型”,中间件执行顺序呈现先入后出的效果。
const Koa = require('koa');
const app = new Koa();
// 中间件 1:外层日志
app.use(async (ctx, next) => {
console.log('1. 进入中间件1');
await next(); // 进入下一个中间件,并挂起当前中间件
console.log('5. 退出中间件1');
});
// 中间件 2:内层日志 + 耗时操作
app.use(async (ctx, next) => {
console.log('2. 进入中间件2');
await new Promise(resolve => setTimeout(resolve, 100)); // 模拟异步
await next();
console.log('4. 退出中间件2');
});
// 中间件 3:响应处理
app.use(async (ctx) => {
console.log('3. 处理响应');
ctx.body = 'Hello Koa';
});
app.listen(3000);
// 控制台输出:1 -> 2 -> (等待100ms) -> 3 -> 4 -> 5
控制要点:
await next()之前的代码是“上游”处理(请求到达时执行)。await next()之后的代码是“下游”处理(响应返回时执行)。- 利用这个模型,可以轻松实现请求计时、权限校验(前置)、响应格式统一(后置)等。
自定义 Pipeline(手写控制)
如果不依赖框架,可以通过一个数组 + 索引来手动控制顺序。
class MiddlewarePipeline {
constructor() {
this.middlewares = [];
}
use(fn) {
this.middlewares.push(fn);
}
// 核心执行函数
execute(req, res) {
let index = 0;
const next = () => {
if (index < this.middlewares.length) {
const middleware = this.middlewares[index++];
// 将 req, res, next 传给中间件
middleware(req, res, next);
}
};
next(); // 启动链
}
}
// 使用示例
const pipeline = new MiddlewarePipeline();
pipeline.use((req, res, next) => {
console.log('A: 开始');
req.data = {};
next(); // 必须手动调用
console.log('A: 结束');
});
pipeline.use((req, res, next) => {
console.log('B: 处理中');
req.data.time = Date.now();
next();
});
pipeline.use((req, res, next) => {
console.log('C: 最终处理');
res.send(`Time: ${req.data.time}`);
});
// 模拟请求
pipeline.execute({ method: 'GET' }, { send: console.log });
// 输出:A: 开始 -> B: 处理中 -> C: 最终处理 -> A: 结束
控制要点:
- 通过维护一个递增的
index,确保中间件按注册顺序依次执行。 next()函数内部调用下一个中间件,形成链式调用。
特殊场景:并行/条件性执行
有时需要更灵活的控制,跳过”或“并行”执行中间件。
1 条件跳过
// 只有特定条件下才执行中间件
app.use((req, res, next) => {
if (req.query.bypass) {
// 跳过当前中间件,直接进入下一个
return next();
}
console.log('执行验证逻辑...');
next();
});
2 并行中间件(手动控制)
在 Express 原生不支持并行中间件,但可以通过 Promise.all 手动实现:
app.use(async (req, res, next) => {
const result = await Promise.all([
fetch('http://service1'),
fetch('http://service2')
]);
req.service1Data = result[0];
req.service2Data = result[1];
next(); // 等两个请求都完成后,再进入下一个中间件
});
如何控制顺序?
| 控制方式 | 对应代码 | 效果 |
|---|---|---|
| 注册顺序 | app.use(A); app.use(B); |
A 总是在 B 之前执行 |
next() 调用时机 |
next() 提前 vs 延后 |
决定上下游处理逻辑 |
| 条件判断 | if(条件) return next() |
跳过或选择中间件 |
| 错误传递 | next(error) |
跳转到错误处理链 |
| 异步等待 | await next() (Koa) |
挂起当前中间件,等内层执行完再继续 |
记住:next 函数是控制流的总开关,只要理解了“谁调用了 next,谁就交出控制权”,就能精确控制任何中间件系统的执行顺序。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播