在Web开发中,中间件(Middleware)是处理HTTP请求和响应的核心机制之一。EggJS基于Koa的洋葱模型实现了高效的中间件机制,本文将深入探讨中间件的执行原理、开发实践以及常见问题解决方案。

一、中间件执行机制与洋葱模型
1. 洋葱模型解析
EggJS继承自Koa的洋葱模型,中间件按照"先进后出"的顺序执行,形成一个类似洋葱的结构:
javascript
// 中间件执行顺序示例
app.use(async (ctx, next) => {
console.log('Middleware 1 Start');
await next();
console.log('Middleware 1 End');
});
app.use(async (ctx, next) => {
console.log('Middleware 2 Start');
await next();
console.log('Middleware 2 End');
});
// 请求处理流程输出:
// Middleware 1 Start → Middleware 2 Start → Controller → Middleware 2 End → Middleware 1 End
执行特点:
- 请求从外到内穿过各层中间件
- 响应从内到外返回时执行剩余逻辑
- 支持异步操作(
async/await
)
二、开发常用中间件实战
1. 日志记录中间件
记录请求基本信息与响应时间:
javascript
// app/middleware/access_log.js
module.exports = (options) => {
return async (ctx, next) => {
const start = Date.now();
await next();
const cost = Date.now() - start;
ctx.logger.info(
`${ctx.method} ${ctx.url} - ${ctx.status} [${cost}ms]`
);
};
};
2. 权限校验中间件
JWT Token验证示例:
javascript
// app/middleware/auth.js
const jwt = require('jsonwebtoken');
module.exports = () => {
return async (ctx, next) => {
const token = ctx.get('Authorization');
try {
const decoded = jwt.verify(token, ctx.app.config.jwtSecret);
ctx.state.user = decoded; // 用户信息挂载到上下文
await next();
} catch (err) {
ctx.status = 401;
ctx.body = { message: 'Invalid token' };
}
};
};
3. 性能监控中间件
上报接口响应时间:
javascript
// app/middleware/performance.js
module.exports = () => {
return async (ctx, next) => {
const start = Date.now();
await next();
const cost = Date.now() - start;
ctx.app.metrics.timing('http_request_duration', cost, {
method: ctx.method,
path: ctx.path,
status: ctx.status
});
};
};
三、中间件配置策略
1. 全局中间件配置
在config/config.default.js
中启用:
javascript
module.exports = {
middleware: ['performance', 'accessLog'],
// 中间件参数配置
accessLog: {
ignorePaths: ['/healthcheck']
}
};
2. 路由级中间件配置
在路由文件中指定:
javascript
// app/router.js
module.exports = app => {
const { router, middleware } = app;
router.get(
'/admin',
middleware.auth(), // 路由中间件
controller.admin.index
);
};
执行顺序:
- 全局中间件按配置数组顺序执行
- 路由级中间件在匹配到路由后执行
四、常见开发陷阱与规避
1. 忘记调用next()
错误示例:
javascript
module.exports = () => {
return async (ctx) => { // 缺少next参数
// 业务逻辑...
// 未执行await next()
};
};
后果:请求被挂起,后续中间件不执行
2. 异步操作处理不当
正确做法:
javascript
module.exports = () => {
return async (ctx, next) => {
await someAsyncTask(); // 必须await异步操作
await next();
};
};
3. 中间件顺序错误
黄金法则:
- 通用中间件(如日志)放在前面
- 业务相关中间件(如权限)放在后面
4. 性能问题优化
避免在中间件中:
- 同步阻塞操作(如大文件读取)
- 高频的远程调用(可改用缓存)
五、最佳实践建议
- 职责单一:每个中间件只处理一个功能
- 合理分层 :
- 全局层:日志、性能监控
- 路由层:权限校验、参数校验
- 错误处理 :使用
try/catch
包裹核心逻辑 - 性能监控:关键中间件添加耗时统计
总结
中间件机制是EggJS框架的核心特性之一,合理使用中间件可以实现以下优势:
- 功能解耦:分离业务与非业务逻辑
- 逻辑复用:通用功能全局生效
- 灵活扩展:按需组合不同中间件
下篇预告:数据库操作与ORM实践
下一篇将深入探讨:
- MySQL基础配置与多环境适配
- Sequelize模型定义与关联关系
- 复杂查询构建与性能优化
- 数据库迁移与种子数据管理
正确理解和应用中间件,可以显著提升EggJS应用的开发效率和可维护性。下一篇文章我们将深入探讨《数据库操作与ORM实践》,解析如何在Egg中高效操作数据库。欢迎在评论区留下你遇见的「中间件」设计经验与挑战,共同探讨最佳实践!