Midway Hooks 函数式中间件实战:useContext 驱动的 Web 中间件体系
后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载Midway Hooks 是 Midway 面向前后端一体化开发的函数式 API 形态midwayjs/hooks它把传统「类控制器 装饰器」的中间件注册方式重构为「普通函数 useContext()」的函数式写法。本文围绕site/versioned_docs/version-3.0.0/hooks/middleware.md展开系统讲解 Hooks 中间件的语法、三种注册粒度全局 / 文件级 / 单函数、Koa 中间件复用方法并结合仓库源码packages/core/src/functional/hooks.ts、packages/core/src/common/webGenerator.ts等剖析其底层原理。读完本文你将能独立编写、挂载、组合任意粒度的 Hooks 中间件并理解它与 Midway 经典中间件体系的对接方式。中间件语法函数 useContext()与 Midway 经典 Web 中间件(ctx, next) {}不同Hooks 中间件是一个只接收next一个参数的异步函数上下文ctx需要通过useContext()获取next指向下游处理流程下一个中间件或最终 Api 函数必须被调用才能让请求继续往下走useContext()从当前异步上下文Async Context中取出请求上下文ctx其底层实现位于 packages/core/src/functional/hooks.ts#L18-L23export function useContextT any(): T | undefined { const ctx getCurrentAsyncContextManager() .active() .getValue(ASYNC_CONTEXT_KEY); return ctx as T; }即通过getCurrentAsyncContextManager().active()获取当前异步上下文的激活状态再按ASYNC_CONTEXT_KEY定义于 packages/core/src/constants.ts#L19值为Symbol(ASYNC_CONTEXT_KEY)读取请求上下文。因此只要请求处理链路仍在同一个异步上下文内中间件与 Api 函数中就能一致地拿到当前请求的ctx。由于中间件本质上是普通函数你可以在其中使用任意 Hooks如useLogger()、useConfig()、useInject()等均定义于 packages/core/src/functional/hooks.ts。例如useLogger()在存在ctx时返回ctx.logger否则回退到主应用 Loggerpackages/core/src/functional/hooks.ts#L25-L35。基础示例记录请求日志以一个请求日志中间件为例它打印请求进入时间与处理耗时import { Context } from midwayjs/koa; import { useContext } from midwayjs/hooks; const logger async (next: any) { const ctx useContextContext(); console.log( -- [${ctx.method}] ${ctx.url} ); const start Date.now(); await next(); const cost Date.now() - start; console.log( -- [${ctx.method}] ${ctx.url} ${cost}ms ); };该中间件展示了完整的洋葱模型时序await next()之前的代码在请求进入时执行await next()之后的代码在响应返回时执行。ctx.method、ctx.url来自 Koa 类型Context说明 Hooks 中间件直接运行在底层 Web 框架Koa的请求上下文之上。全局中间件作用于所有接口全局中间件在src/configuration.ts中通过createConfigurationhooks({ middleware })定义对所有接口生效import { hooks, createConfiguration, } from midwayjs/hooks; import logger from ./logger; // Global Middleware export default createConfiguration({ imports: [ hooks({ middleware: [logger], }), ], });仓库中的真实示例可见 site/example/function/src/apis/configuration.ts它在全局挂载了koa-bodyparserimport { hooks, createConfiguration } from midwayjs/hooks; import bodyParser from koa-bodyparser; export default createConfiguration({ imports: [ hooks({ middleware: [bodyParser()], }), ], });hooks()返回的组件配置会在 Midway 启动时被扫描、注册middleware数组中的每一项都会进入该 Web 应用的中间件链从而对 Hooks 注册的所有路由生效。文件级中间件作用于单个 Api 文件文件级中间件定义在 Api 文件中通过导出的config.middleware声明对该文件内的所有 Api 函数生效import { ApiConfig, Api, Get, } from midwayjs/hooks; import logger from ./logger; // File Level Middleware export const config: ApiConfig { middleware: [logger], }; export default Api(Get(), async () { return Hello World!; });其中ApiConfig是 Midway Hooks 约定的文件级配置类型。这种粒度非常适合对一组相近接口统一注入鉴权、参数校验、限流等中间件而无需逐个函数声明。单函数中间件作用于单个 Api 函数通过Middleware(...middlewares)声明的中间件仅对单个函数生效粒度最细import { Api, Get, Middleware, } from midwayjs/hooks; import logger from ./logger; export default Api( Get(), Middleware(logger), async () { return Hello World!; } );Middleware()接收一个或多个中间件作为Api()的第二个参数传入位置在 HTTP 方法装饰器Get()之后、处理函数之前。从源码实现看单函数中间件会被收集到路由定义route.options.middleware中。createRouteBuilder初始化路由时默认给出middleware: []packages/core/src/functional/api.ts#L202-L210随后在defineApi中通过RequestMapping把route.options.middleware写入路由元数据packages/core/src/functional/api.ts#L479-L487最终由 Web 路由服务装配为实际的中间件链。直接复用 Koa 中间件Hooks 中间件与 Koa 中间件协议一致async (ctx, next) {}因此你可以在上述三种粒度中直接传入任意 Koa 中间件生态无缝衔接。以koa/cors为例全局启用import { hooks, createConfiguration, } from midwayjs/hooks; import logger from ./logger; import cors from koa/cors; // Global Middleware export default createConfiguration({ imports: [ hooks({ middleware: [logger, cors()], }), ], });文件级启用import { ApiConfig, Api, Get, } from midwayjs/hooks; import logger from ./logger; import cors from koa/cors; // File Level Middleware export const config: ApiConfig { middleware: [logger, cors], }; export default Api(Get(), async () { return Hello World!; });函数级启用import { Api, Get, Middleware, } from midwayjs/hooks; import logger from ./logger; import cors from koa/cors; export default Api( Get(), Middleware(logger, cors), async () { return Hello World!; } );注意koa/cors属于工厂函数形态全局挂载时需要执行cors()生成实例文件级/函数级数组声明中传入的则是中间件函数本身。同理类似koa-bodyparser这类工厂中间件在全局配置中也要执行一次见 site/example/function/src/apis/configuration.ts 中的bodyParser()用法。仓库站点文档另有 site/docs/hooks/cors.md 对跨域场景做专题说明。底层原理中间件链的组装与执行从路由元数据到真实中间件链无论中间件声明在哪一级最终都会归一到路由级元数据文件级config.middleware与单函数Middleware()在路由生成阶段被合并进route.options.middleware全局hooks({ middleware })则作用于整个应用层。在 Web 路由生成阶段packages/core/src/common/webGenerator.ts#L126-L148框架会对每个 controller 与每条 route 的中间件数组调用middlewareService.compose(...)组装为可执行的中间件链并分别挂载到newRouter.use(...)与路由方法上。compose正是 koa-compose 风格的洋葱模型编排器其行为由 packages/core/test/service/middlewareService.test.ts 中的大量用例覆盖包括中间件数组为空时的兜底should work with 0 middleware、中间件抛错时的错误传播should reject on errors in middleware、嵌套组合、以及不会污染原中间件数组should not affect the original middleware array等。请求上下文的传递Hooks 中间件之所以能只用next一个参数就能访问ctx关键在useContext()依赖的异步上下文Async Context机制。Midway 在请求入口将ctx以ASYNC_CONTEXT_KEY为键写入异步上下文管理器随后在中间件与 Api 函数执行的整条链路上都能读取到同一份上下文。这也意味着中间件可以安全地在await next()前后读写ctx如设置响应头、记录耗时若某个中间件不调用next()请求会在该处被终结例如鉴权失败直接返回 401中间件顺序即数组顺序先注册的先执行先执行的await next()之前代码先运行之后代码后运行洋葱模型。与 Midway 经典中间件的异同经典写法midwayjs/core的类中间件通过Middleware()装饰器声明类实现resolve()返回(ctx, next) {}见 packages/core/src/decorator/common/middleware.ts#L4Hooks 函数式写法把中间件简化为普通异步函数配合useContext()免去了ctx参数的传递与 Hooks 的函数式 Api 风格保持统一。两者底层都汇入同一条中间件链webGeneratormiddlewareService.compose因此可以混用也都能直接消费 Koa 生态中间件。实践建议三种粒度如何选择粒度声明位置生效范围典型场景全局中间件configuration.ts的hooks({ middleware: [...] })所有接口请求日志、CORS、Body 解析、全局异常处理文件级中间件Api 文件导出config: ApiConfig { middleware: [...] }文件内所有 Api按模块统一鉴权、参数校验、限流单函数中间件Api(Get(), Middleware(...), handler)单个 Api 函数单接口特例处理、局部权限控制选择原则能全局统一处理的放全局需要按模块区分的用文件级仅个别接口特殊的用单函数级。三者可叠加使用实际执行顺序由注册先后与路由挂载顺序共同决定。由于中间件支持任意 Hooks如useLogger、useConfig你还可以把日志、配置读取、依赖注入等逻辑封装进中间件进一步收敛横切关注点。小结Midway Hooks 用「函数 useContext()」重新定义了 Web 中间件的编写方式语法上只有一个next参数上下文通过异步上下文机制透明注入作用域上提供全局、文件级、单函数三种粒度可灵活组合生态上直接兼容 Koa 中间件。其底层实现useContext的 Async Context 读取、webGenerator的中间件链组装、middlewareService.compose的洋葱编排与 Midway 经典中间件体系同源既保证了函数式 Api 的开发体验又不牺牲框架既有的中间件能力。掌握这一套中间件体系即可在 Hooks 应用中系统地落实日志、鉴权、跨域、校验等横切需求。赞分享后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载相关推荐Midway Hooks 函数式 Web 中间件实战从日志记录到 Koa 生态复用Midway Hooks 函数式 Web 中间件实战从日志记录到 Koa 生态复用 Midway Hooks 是 Midway 框架面向前端/全栈开发者的函数后端微服务云原生Midway Hooks Web 中间件完全指南从全局到单函数的三级作用域实战Midway Hooks Web 中间件完全指南从全局到单函数的三级作用域实战 Midway Hooks 是 Midway 的全栈函数式开发范式它打破了传统后端微服务云原生Midway Hooks 中间件完全指南从函数式语法到 Koa 生态复用Midway Hooks 中间件完全指南从函数式语法到 Koa 生态复用 本文围绕 Midway Hooks 的 Web 中间件体系展开讲解如何用「函数 后端微服务云原生上一篇8cc一个小型C编译器下一篇MCSManager与Steam游戏服务器全面支持Palworld、Squad等热门游戏创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考