Egg中间件

发布时间:2026/11/4 10:55:52

Egg中间件 下面按“它是什么 → 什么时候加载 → 什么触发 → 原理 → 如何开发”来说明 Egg 的HTTP 中间件。1. Egg 中间件是什么Egg 基于 Koa所以 Egg 的中间件本质上就是Koa 中间件asyncfunctionmiddleware(ctx,next){// 请求进入时执行awaitnext();// 暂停当前中间件把请求交给后面的中间件或业务接口处理并等待它们处理完成。// 后续中间件、路由、Controller 执行完后再回来执行}它主要用于处理请求链路中的通用逻辑例如日志记录鉴权错误处理请求体解析响应压缩跨域处理请求耗时统计统一响应包装在 Egg 里一个完整的中间件通常是一个工厂函数module.exports(options,app){returnasyncfunctionmyMiddleware(ctx,next){awaitnext();};};其中options来自app.config[中间件名]app是当前 Egg Application 实例返回值必须是一个 Koa 风格的中间件函数(ctx, next) {}2. 中间件什么时候加载Egg 的普通 HTTP 中间件是在应用启动阶段加载的不是请求来了才读取文件。在当前仓库代码里App Worker 的加载顺序大致是loadApplicationExtend loadRequestExtend loadResponseExtend loadContextExtend loadHelperExtend loadCustomLoader loadCustomApp loadService loadMiddleware loadController loadRouter也就是说先加载扩展、插件、配置等加载app.js/app.ts加载 Service加载 Middleware加载 Controller加载 Router。对应源码位置egg/packages/egg/src/lib/loader/AppWorkerLoader.tsegg/packages/core/src/loader/egg_loader.ts中间件加载时发生了什么Egg 的loadMiddleware()会做几件事扫描各个 load unit 下的app/middleware目录例如应用自己的app/middleware插件的app/middleware框架内置的app/middleware把中间件工厂函数加载到app.middlewares同时也暴露到app.middleware所以你在路由里常见这种写法app.router.get(/foo,app.middleware.auth(),controller.foo.index);按顺序读取app.config.coreMiddleware.concat(app.config.appMiddleware)也就是框架 / 插件核心中间件 → 应用中间件对每个中间件执行工厂函数constmwcreateMiddleware(options,app);包装enable、match、ignore等通用配置。调用app.use(mw);把中间件加入 Koa 的中间件栈。最后 Egg 会把 Router 中间件追加到最后app.use(app.router.middleware());所以全局中间件通常会在路由和 Controller 之前执行。3. 什么触发中间件HTTP 请求触发中间件。当请求进入 Egg 应用时大致流程是Node.js HTTP Server ↓ app.callback() ↓ 创建 ctx ↓ koa-compose 组合 app.middleware 数组 ↓ 按顺序执行中间件 ↓ router middleware ↓ 匹配路由 ↓ 路由级中间件 ↓ Controller ↓ 响应返回中间件按洋葱模型回退也就是说中间件不是被你手动调用的而是请求进入应用后由 Koa/Egg 的请求处理链自动触发。全局中间件如果通过配置启用// config/config.default.jsmodule.exports{middleware:[gzip],};那么这个中间件会参与每一次 HTTP 请求除非配置了match/ignore。路由级中间件如果只挂载在某个路由上app.router.get(/admin,app.middleware.auth(),controller.admin.index);那么只有请求匹配/admin这条路由时才会触发这个中间件。4. Egg 中间件的核心原理是什么核心原理是 Koa 的洋葱圈模型和koa-compose。假设有三个中间件app.use(mw1);app.use(mw2);app.use(mw3);每个中间件都写成asyncfunctionmw(ctx,next){console.log(before);awaitnext();console.log(after);}请求执行顺序是mw1 before mw2 before mw3 before router / controller mw3 after mw2 after mw1 after这就是“洋葱模型”。await next()的作用next()表示把控制权交给下一个中间件。例如module.exports(){returnasyncfunctionauth(ctx,next){if(!ctx.session.user){ctx.status401;ctx.bodyUnauthorized;return;}awaitnext();};};这里如果没有登录就直接return后面的中间件、路由和 Controller 都不会执行。所以中间件可以放行请求await next()截断请求不调用next()在后续处理完成后修改响应await next()之后处理ctx.body5. 如何开发一个 Egg 中间件以一个请求耗时统计中间件为例。第一步创建中间件文件// app/middleware/request_time.jsmodule.exports(options,app){returnasyncfunctionrequestTime(ctx,next){conststartDate.now();try{awaitnext();}finally{constcostDate.now()-start;// 记录每次请求耗时便于排查慢接口和性能问题。app.logger.info([request-time] %s %s %sms,ctx.method,ctx.url,cost);if(options.headerName){// 将耗时写入响应头方便本地调试或网关侧观测。ctx.set(options.headerName,String(cost));}}};};注意文件名request_time.js会被转换成中间件名requestTime。第二步在配置中启用// config/config.default.jsmodule.exports{middleware:[requestTime],requestTime:{headerName:x-response-time,},};这里middleware:[requestTime]表示启用这个应用中间件。requestTime:{headerName:x-response-time,}会作为options传入module.exports(options,app){}第三步使用通用配置控制生效范围关闭中间件module.exports{requestTime:{enable:false,},};只匹配某些路径module.exports{requestTime:{match:/api,},};表示只对/api前缀的请求生效。忽略某些路径module.exports{requestTime:{ignore:/health,},};表示/health请求不会经过这个中间件。使用函数动态判断module.exports{requestTime:{match(ctx){// 只对带 debug 参数的请求启用耗时响应头。returnctx.query.debug1;},},};注意match和ignore不能同时配置。6. 如何只在某个路由使用中间件如果不想全局启用可以不写config.middleware而是在路由中手动挂载// app/router.jsmodule.exportsapp{constauthapp.middleware.auth({role:admin,});app.router.get(/admin/users,auth,app.controller.admin.users);};这种方式下auth不会全局执行只有请求命中/admin/users时才会执行适合鉴权、权限校验、局部限流等场景。7. 框架 / 插件中间件和应用中间件的区别Egg 里主要有两类全局中间件1. Core Middleware框架或插件提供的中间件配置在app.config.coreMiddleware当前框架默认包含类似[meta,siteFile,notfound,bodyParser,overrideMethod]它们会比应用中间件更早加载。2. App Middleware应用自己配置的中间件来自config.middleware在加载配置时Egg 会把它转换成app.config.appMiddleware最终加载顺序是coreMiddleware → appMiddleware → routerMiddleware也就是框架/插件中间件 → 应用中间件 → 路由分发8. 开发中间件时的注意事项1. 一定要返回函数错误写法module.exports(options,app){// 忘了 return middleware};正确写法module.exports(options,app){returnasyncfunctionmyMiddleware(ctx,next){awaitnext();};};2. 不要忘记await next()如果你希望请求继续到后续中间件和 Controller必须调用awaitnext();否则请求会被当前中间件截断。3. 适合在await next()前做请求前置处理例如returnasyncfunctionauth(ctx,next){// 请求进入 Controller 前校验身份。if(!ctx.session.user){ctx.status401;return;}awaitnext();};4. 适合在await next()后做响应后置处理例如returnasyncfunctionresponseWrapper(ctx,next){awaitnext();// Controller 执行完成后统一包装响应。ctx.body{success:true,data:ctx.body,};};5. 当前 Egg 代码已不支持 generator 中间件旧式写法类似function*middleware(next){}当前代码中如果检测到 generator middleware会直接抛错。应使用 async function。9. 一句话总结Egg 中间件就是 Koa 风格的请求处理函数。它在应用启动时从app/middleware加载根据coreMiddleware和middleware配置加入 Koa 中间件栈每次 HTTP 请求进入app.callback()后被koa-compose按洋葱模型触发执行开发时只需要编写(options, app) async (ctx, next) {}然后在配置或路由中挂载即可。AGENTS.md 检查结果已按仓库要求检查是否需要更新AGENTS.md。本次是对 Egg 中间件机制的解释性问答没有产生新的稳定开发约定、架构规则或可复用流程变更因此本次未发现需要更新 AGENTS.md 的稳定信息。
延伸阅读

更多相关文章

2026/11/3 22:12:29

阿里开源Java八股文终极版:系统性刷题与面试进阶指南

"阿里官方上线!号称国内Java八股文天花板(终极版)首次开源"这个消息一出来,我朋友圈里瞬间炸了锅。做Java的、准备跳槽的、带新人的,几乎都在转这个资源。我第一次看到标题的时候,第一反应是&quo…

2026/11/1 16:47:27

欢聚时代校招PHP笔试C卷解析:PHP 7、MySQL与Redis核心考点全覆盖

1. 先聊聊这份卷子的出题思路说说欢聚时代。当年这家公司在直播、游戏、社交赛道都铺得很开,技术团队里PHP的占比非常高,尤其是业务层几乎全是PHP的天下。所以校招笔试C卷能非常直观地反映出一线互联网公司对一个准PHP工程师的底层要求——不考花架子&am…

2026/11/4 5:14:18

Python零基础学习路线:爬虫与数据分析实战指南

看到“全748集 Python 零基础全套教程,七天学完即可就业”这种标题,第一反应通常有两种:要么觉得是夸张营销,要么觉得这么多集根本看不完。但如果你真的想从零开始学 Python,目标又是爬虫和数据分析,这套合…

2026/11/4 10:55:11

网络安全学习路线:从TCP/IP到渗透测试实战

1. 网络安全学习路线全景解析作为一名从业多年的网络安全工程师,我经常被问到这样一个问题:"网络安全到底应该从哪开始学?"今天我就用最接地气的方式,把我这些年摸爬滚打总结出的学习路线和实战经验分享给大家。1.1 为什…

2026/11/4 10:55:11

消息队列技术选型与RocketMQ架构解析

1. 为什么需要消息队列:从业务场景到技术选型 消息队列(MQ)作为分布式系统的核心组件,其价值远不止于"发消息"这么简单。让我们从一个真实的电商场景切入: 用户完成一笔订单后,系统需要依次执行…

2026/11/4 10:55:11

Git交互式变基(rebase -i)完全指南:从入门到精通

1. 理解git rebase -i的核心价值第一次接触git rebase -i HEAD~n这个命令时,我正深陷在一个包含十几个混乱提交的分支里。那些"临时保存"、"修复小问题"、"再试一次"的提交消息像杂草一样丛生,而rebase -i就像一把精准的园…

2026/11/4 10:55:11

MySQL架构解析与生产环境部署指南

1. MySQL架构深度解析 1.1 软件架构:C/S模型详解 MySQL采用经典的客户端/服务器架构(Client/Server),这种设计模式在现代数据库系统中非常普遍。让我们用一个更贴近开发实际的例子来理解: 想象你正在开发一个电商网站…

2026/11/4 10:55:11

Claude-Code国内使用指南:解决访问与API集成难题

1. 项目概述 "Claude-Code国内使用教程"这个标题指向的是一个非常实用的技术指南,主要解决国内开发者在访问和使用Claude-Code服务时可能遇到的各种实际问题。作为一个长期关注开发者工具生态的技术博主,我发现很多国内同行在尝试使用这类海外…

2026/11/4 10:50:10

哈希表原理与LeetCode实战指南

1. 哈希表理论基础与实战应用哈希表(Hash Table)是算法面试和工程实践中最重要的数据结构之一。作为从业多年的工程师,我见过太多因为不理解哈希表底层原理而导致的性能问题和逻辑错误。今天我们就从底层实现到LeetCode实战,彻底掌…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/4 0:03:20

AI辅助技术栈评估:提升研发决策效率60%

1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年…

2026/11/4 0:03:20

从生成Token到直接决策:Jev如何重塑AI Agent工程实践

1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你…

2026/11/4 0:03:20

32G统一内存跑大模型:内存账、TPS实测与端云分流

想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑