- 基本概念
- 中间件是一个函数,可以访问请求对象(req)、响应对象(res)和next函数
- 按照顺序执行,从上到下依次调用
- 可以执行任何代码、修改请求和响应对象、结束请求-响应循环、调用下一个中间件。如果当前中间件没有终结请求,并且next()没有被调用,那么请求将被挂起,后面定义的中间件将不会被执行。
- 中间件的主要用途就是处理HTTP请求,用来完成特定的任务如登录状态验证、请求日志、错误处理、Cookie等。
- 中间件类型
// 应用级中间件
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
// 路由级中间件
app.use('/user', (req, res, next) => {
// 只处理/user路径的请求
next();
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
// 内置中间件
app.use(express.json()); // 解析JSON
app.use(express.static('public')); // 静态文件
// 第三方中间件
app.use(cors()); // 处理跨域
- 执行流程示例
app.use((req, res, next) => {
console.log('1. 首先经过这个中间件');
next(); // 调用下一个中间件
});
app.use((req, res, next) => {
console.log('2. 然后是这个中间件');
next();
});
app.get('/api', (req, res) => {
console.log('3. 最后到达路由处理器');
res.send('Hello');
});
- 常见使用场景
// 身份验证
app.use((req, res, next) => {
if (!req.headers.authorization) {
return res.status(401).json({ message: '未授权' });
}
next();
});
// 日志记录
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// 错误处理
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('服务器错误');
});
- 自定义中间件示例
// 计时中间件
const timeMiddleware = (req, res, next) => {
req.startTime = Date.now();
next();
console.log(`请求处理时间: ${Date.now() - req.startTime}ms`);
};
// 使用中间件
app.use(timeMiddleware);
为什么这么设计?
将请求处理函数进行模块化分解后的若干子处理函数,一些列子处理函数可以形成一个中间件堆栈
// Request flow through middleware chain
app.use(loggerMiddleware) // 1. Log request
.use(authMiddleware) // 2. Check auth
.use(parseJsonBody) // 3. Parse body
.get('/api', handler); // 4. Handle route
好处:
- 模块化: 每个中间件只负责单一功能
- 可复用: 中间件可在不同路由复用
- 灵活性: 可根据需求动态添加/移除中间件
- 维护性: 职责分离便于维护和测试
- 扩展性: 易于添加新功能而不影响现有代码
职责分离例子:
// Authentication middleware
const auth = (req, res, next) => {
verifyToken(req.headers.token)
.then(() => next())
.catch(() => res.status(401).send());
}
// Business logic stays clean
app.get('/api', auth, (req, res) => {
// Only handle business logic here
res.json(data);
});
错误处理链式传递:
app.use((err, req, res, next) => {
if (err.type === 'auth') {
res.status(401).send();
} else {
next(err); // Pass to next error handler
}
});
这种设计模式让代码更容易维护、测试和扩展,同时保持了良好的关注点分离。