如果你说的是 Node.js / Express 的中间件(Middleware)原理,核心其实就一句话:
Express 把一系列函数按顺序串起来,每个函数通过
next()把控制权交给下一个函数。
1. 最简单的模型
javascript
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('middleware 1');
next();
});
app.use((req, res, next) => {
console.log('middleware 2');
next();
});
app.get('/', (req, res) => {
console.log('route handler');
res.send('Hello');
});
app.listen(3000);
请求 / 时:
scss
Request
↓
middleware 1
↓ next()
middleware 2
↓ next()
route handler
↓
Response
所以中间件本质上就是:
lua
(req, res, next) => {}
2. next() 到底是什么?
可以把 Express 的内部机制简化成:
ini
const middlewares = [
middleware1,
middleware2,
middleware3
];
function handle(req, res) {
let index = 0;
function next() {
const middleware = middlewares[index++];
if (!middleware) return;
middleware(req, res, next);
}
next();
}
于是:
lua
function middleware1(req, res, next) {
console.log('1');
next();
}
function middleware2(req, res, next) {
console.log('2');
next();
}
function middleware3(req, res, next) {
console.log('3');
next();
}
实际上就是:
scss
next()
↓
找到下一个 middleware
↓
middleware(req, res, next)
↓
middleware 内部再次 next()
↓
继续找下一个
这就是 Express 中间件最核心的原理。
3. 为什么中间件可以修改 req?
因为所有中间件拿到的是同一个 req 对象。
例如:
ini
app.use((req, res, next) => {
req.user = {
id: 123,
name: 'Tom'
};
next();
});
app.get('/', (req, res) => {
console.log(req.user);
res.send('OK');
});
执行过程:
ini
┌──────────────────┐
Request │ req │
│ res │
└────────┬─────────┘
↓
middleware 1
│
req.user = ...
↓
middleware 2
│
读取 req.user
↓
response
所以很多 Express 功能本质上都是:
markdown
前置 middleware
↓
修改 req / res
↓
后续 middleware
↓
业务 handler
4. next() 不调用会怎样?
例如:
javascript
app.use((req, res, next) => {
console.log('hello');
});
这里既没有:
scss
next();
也没有:
ini
res.send(...);
那么请求就会停在这里。
vbscript
Request
↓
middleware
↓
❌ 卡住
所以中间件通常有两个选择:
继续执行
scss
next();
结束请求
arduino
res.send('Hello');
5. Express 中间件为什么能处理异常?
还有一个非常重要的设计:
javascript
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('Server Error');
});
这种有 4 个参数:
lua
(err, req, res, next)
Express 会把它识别为错误处理中间件。
例如:
javascript
app.use((req, res, next) => {
try {
throw new Error('Something wrong');
} catch (err) {
next(err);
}
});
执行:
vbscript
middleware
│
│ next(error)
↓
跳过普通 middleware
↓
错误处理中间件
↓
response
也就是说:
scss
next()
表示:
正常进入下一个 middleware
而:
scss
next(err)
表示:
出错了,请进入错误处理链。
6. 中间件其实是一个"责任链"
从设计模式角度看,Express Middleware 很像:
Chain of Responsibility(责任链模式)
例如一个真实项目:
php
app.use(cors());
app.use(logger);
app.use(auth);
app.use(rateLimit);
app.use(express.json());
app.use('/api', router);
请求:
vbscript
HTTP Request
↓
CORS
↓
Logger
↓
Auth
↓
RateLimit
↓
JSON Parser
↓
Router
↓
Controller
↓
Service
↓
Database
↓
HTTP Response
每一层都可以:
- 修改请求
- 修改响应
- 终止请求
- 继续请求
- 抛出错误
7. 一个容易忽略的点:next() 之后的代码
看这个:
javascript
app.use((req, res, next) => {
console.log('A');
next();
console.log('B');
});
app.use((req, res, next) => {
console.log('C');
next();
console.log('D');
});
结果是:
css
A
C
D
B
为什么?
因为调用:
scss
next();
实际上相当于进入下一层函数调用。
可以想象成:
javascript
function middleware1() {
console.log('A');
middleware2();
console.log('B');
}
function middleware2() {
console.log('C');
// next 后继续执行
console.log('D');
}
所以 Middleware 不只是:
1 → 2 → 3
更准确地说是:
scss
middleware 1
↓
next()
↓
middleware 2
↓
next()
↓
middleware 3
↓
return
↑
middleware 2
↑
middleware 1
这也是理解 Express 中间件非常关键的一点。
8. 用一个极简版本自己实现 Express
如果你想真正理解源码,可以自己实现一个 Mini Express:
scss
function createApp() {
const middlewares = [];
function use(fn) {
middlewares.push(fn);
}
function handle(req, res) {
let index = 0;
function next(err) {
const fn = middlewares[index++];
if (!fn) {
return;
}
if (err) {
// 错误处理中间件
if (fn.length === 4) {
return fn(err, req, res, next);
}
return next(err);
}
if (fn.length === 4) {
return next();
}
fn(req, res, next);
}
next();
}
return {
use,
handle
};
}
然后:
scss
const app = createApp();
app.use((req, res, next) => {
console.log('middleware 1');
next();
});
app.use((req, res, next) => {
console.log('middleware 2');
next();
});
这已经抓住了 Express Middleware 最核心的思想。
你可以记住这张脑图
scss
Express
│
Middleware Stack
│
┌───────────┼───────────┐
↓ ↓ ↓
fn(req) fn(req) fn(req)
│ │ │
└── next() ─┘ │
↓
Route Handler
│
res
如果你正在准备 Node.js / Express 面试 ,下一步最值得搞懂的是 app.use()、Router、中间件匹配规则,以及 Express 4/5 的 next() / 异步错误处理机制。