资讯动态

H3 中间件完全指南:用 `app.use` 拦截请求、响应与错误

发布时间:2026/9/17 12:45:15 来源:尧图企业网站定制
H3 中间件完全指南用app.use拦截请求、响应与错误【免费下载链接】h3⚡️ Minimal H(TTP) framework built for high performance and portability项目地址: https://gitcode.com/GitHub_Trending/h31/h3中间件是 H3 中拦截请求、响应和错误的核心机制它在每次请求到达路由处理器之前以包装器wrapper形式执行通过next()穿透整个调用链。读完本文你将掌握全局中间件与路由级中间件的注册方式、next()的洋葱模型语义、基于路由/方法/自定义条件的匹配过滤以及onRequest、onResponse、onError三个内置工厂函数的实战用法并深入理解 src/middleware.ts 中的底层调度实现。中间件在 H3 中的定位H3 的中间件是一段在每个请求上、于路由处理器之前运行的函数作为包装器拦截请求、响应和错误。在请求生命周期中3. Dispatch Request阶段正是中间件发挥作用的位置H3 根据request.url和request.method匹配路由依次调用全局中间件最后才调用匹配到的路由处理器。从类型定义看一个中间件就是接收event和next两个参数的函数src/types/handler.tsexport type Middleware ( event: H3Event, next: () MaybePromiseunknown | undefined, ) MaybePromiseunknown | undefined;event当前请求的 H3Event 实例包含event.req、event.url、event.context等next调用下一个中间件或最终的路由处理器返回其原始返回值返回值可以是任意值将作为响应体发送也可以是undefined或next()的结果表示继续传递。[!IMPORTANT] H3 官方强烈建议尽可能优先使用组合式工具函数composable utilities全局中间件会让应用逻辑变得不那么可预测、更难以理解。中间件应当用于横切关注点日志、鉴权、限流等而不是替代业务逻辑。使用app.use注册全局中间件全局中间件通过H3.use注册到应用实例上见 H3 API 参考 与 src/h3.ts 的实现。use支持两种调用形式use(route: string, handler: Middleware | H3, opts?: MiddlewareOptions): this; use(handler: Middleware | H3, opts?: MiddlewareOptions): this;基础示例记录每个请求import { H3 } from h3; const app new H3(); app.use((event) { console.log(event); });这里中间件只接收event一个参数不声明next执行完毕后返回undefinedH3 会继续调用下一个中间件/路由处理器因此它纯粹是一个旁路观察者。匹配特定请求use的第二个重载允许传入路由前缀和选项对象实现按route路由、methodHTTP 方法、match自定义函数三种条件的组合匹配app.use( /blog/**, (event, next) { console.log([alert] POST request on /blog paths!); }, { method: POST, // match: (event) event.req.method POST, }, );选项类型定义在 src/types/h3.tsexport type MiddlewareOptions { method?: string; match?: (event: H3Event) boolean; };route路由模式支持/blog/**这类通配符与路由注册使用同一套rou3匹配引擎和normalizeRoute规范化逻辑method限定 HTTP 方法源码中会通过.toUpperCase()统一大小写后比较src/middleware.tsmatch完全自定义的谓词函数返回false则跳过该中间件。一个值得注意的细节GET作用域的中间件同样会命中HEAD请求。这是因为 HEAD 由 GET 处理器提供服务RFC 9110src/middleware.ts中的匹配器显式处理了这一情况// HEAD is served by GET handlers (RFC 9110), so GET-scoped middleware also matches HEAD if (reqMethod ! method !(method GET reqMethod HEAD)) { return false; }该行为在 test/middleware.test.ts 中有对应测试用例GET-scoped global middleware also runs for HEAD requests验证。另外当路由匹配成功时路由中的参数如/user/:id的id会被合并进event.context.middlewareParamssrc/middleware.ts中间件内可通过event.context.middlewareParams读取测试用例exposes rou3 param names in middlewareParams同样覆盖了这一点。三种匹配条件的执行优先级从createMatcher的实现src/middleware.ts可以还原匹配顺序先比较method不匹配直接返回false再执行自定义match回调返回false则跳过最后用createRouteMatcher匹配路由路径并把捕获的参数写入event.context.middlewareParams。三个条件在use时通过选项对象任意组合全部满足才会执行中间件。next()与响应拦截洋葱模型的核心当中间件声明第二个参数next时它就可以拦截下一个中间件和路由处理器的返回值app.use(async (event, next) { const rawBody await next(); // [intercept response] —— 在这里可以观察/改写响应体 return rawBody; });这种写法形成了经典的洋葱模型请求依次穿过外层中间件进入内层处理器返回值再原路逐层返回。你可以在await next()之后统一加工响应例如给 JSON 响应包一层结构、统计耗时、注入公共字段等。返回值语义什么情况下会立即响应[!IMPORTANT] 如果中间件返回了除undefined或next()结果之外的值它会立即拦截请求处理并发送响应后续中间件和路由处理器不再执行。app .use(() Middleware 1) .use(() Middleware 2) .get(/, Hello);上面的链式注册中第一个中间件直接返回字符串Middleware 1——这不是undefined也不是next()的结果——因此请求被立即拦截无论请求什么路径响应永远是Middleware 1。注意use支持链式调用返回this。源码层面这一语义由 src/middleware.ts 的callLayer实现中间件返回值ret若为undefined或内部哨兵值kNotFoundSymbol.for(h3.notFound)定义于 src/response.ts则视为未处理自动调用next()继续否则直接作为响应返回const ret fn(event, next); return isUnhandledResponse(ret) ? next() : /* Promise 则等待解析后再判断 */ ...同时next具备幂等保护nextCalled标志确保next()在同一个中间件内最多执行一次重复调用会返回首次调用的结果src/middleware.ts避免破坏调用链。路由级中间件只随特定路由运行全局中间件作用于所有请求当添加路由时你也可以注册只在该路由上运行的中间件。H3.on/H3.get等方法接受第三个参数RouteOptionssrc/types/h3.tsexport type RouteOptions { middleware?: Middleware[]; meta?: H3RouteMeta; };将中间件数组传入middleware字段即可import { basicAuth } from h3; app.get( /secret, (event) { /* 受保护的路由处理器 */ }, { middleware: [basicAuth({ password: test })], }, );这里basicAuth是 H3 内置的组合式认证工具源码见 src/utils/auth.ts支持username、password、validate自定义校验函数、realm认证域默认auth等选项校验失败时会抛出 401 相关错误。此外也可以使用defineHandler在处理器内部声明middleware字段src/types/handler.ts。从执行顺序看请求会先经过所有全局中间件再进入路由级中间件最后到达处理器本身。H3Core[~getMiddleware]src/h3.ts拼接了两者~getMiddleware(_event, route): Middleware[] { const routeMiddleware route?.data.middleware; const globalMiddleware this[~middleware]; return routeMiddleware ? [...globalMiddleware, ...routeMiddleware] : globalMiddleware; }而routeHandlersrc/h3.ts会在路由首次被命中时用composeHandler把路由级中间件与处理器预组合成一个整体并缓存在路由对象的~composed字段上后续请求直接复用。test/middleware.test.ts 的用例完整验证了执行顺序(event) async (event) async (event, next) async (event, next) (passthrough) (event, next) route (register) route (define)先是 5 个全局中间件按注册顺序执行然后是注册路由时传入的middleware最后是defineHandler内声明的middleware。内置工厂函数onRequest、onResponse、onError为方便起见H3 提供了三个中间件工厂函数导出自 src/utils/middleware.ts并由 src/index.ts 统一导出import { onRequest, onResponse, onError } from h3; app.use( onRequest((event) { console.log([${event.req.method}] ${event.url.pathname}); }), ); app.use( onResponse((response, event) { console.log([${event.req.method}] ${event.url.pathname} ~, response.status); }), ); app.use( onError((error, event) { console.log([${event.req.method}] ${event.url.pathname} !! ${error.message}); }), );onRequest请求进入时签名onRequest(hook: (event: H3Event) MaybePromisevoid): Middleware。它在每个请求到达时异步执行钩子不参与响应拦截适合记录访问日志、注入请求上下文。onResponse响应生成后签名onResponse(hook: (response: Response, event: H3Event) unknown): Middleware。它内部先await next()拿到原始返回值再经toResponse转换为Response对象把(response, event)交给钩子钩子若返回新的 Response 即可替换原响应否则沿用原响应src/utils/middleware.tsreturn async function _onResponseMiddleware(event, next) { const rawBody await next(); const response await toResponse(rawBody, event); const hookResponse await hook(response, event); return hookResponse || response; };onError错误发生时签名onError(hook: (error: HTTPError, event: H3Event) unknown): Middleware。它包裹next()捕获链中抛出的任何错误规范化为HTTPError非 HTTP 错误会标记error.unhandled true并保留原始堆栈再交给钩子钩子返回非undefined的值即可优雅处理错误如返回自定义错误响应否则重新抛出src/utils/middleware.ts。完整的可运行示例可参考 examples/middleware.mjs它在一个debug: true的 H3 应用上同时挂载了三个工厂中间件并对/error路由抛出的 500 错误做了日志记录。与全局 Hooks 的区别onRequest/onResponse/onError与new H3({ onRequest, onResponse, onError })配置中的全局 Hooks 功能相似但有一个关键差异全局 Hooks 只在主 H3 应用上运行不会在挂载的子应用中生效见 H3 API 参考 的说明。而中间件可以随app.use注册在任何层级组合更灵活——这也正是官方推荐用中间件实现全局逻辑的原因。另外utils/middleware.ts还提供了bodyLimit(limit)中间件可在读取请求体时强制执行字节数限制超限在消费时表现为413错误。深入底层中间件链的调度与性能设计了解背后的实现可以帮你写出更高效的中间件。H3 在 src/middleware.ts 中做了三处关键优化1.normalizeMiddleware的快路径。如果中间件没有声明route/method/match匹配条件且它是异步函数或声明了两个参数需要next则直接原样返回不做任何包装src/middleware.ts只有带匹配条件的一参数中间件才被包装为不匹配则调用next()的形式。2. 预组合precomposition。composeMiddleware在注册阶段就把整条中间件列表编译成一个单函数调用链把每层的派发成本从每个请求摊薄到每次列表变更请求到来时只需执行一次编译好的链。该缓存app[~composed]、app[~dispatch]会在use()和mount()时主动失效重建src/h3.ts。测试与基准位于 test/bench/。3.toMiddleware的互操作性。H3 还能把任意HTTPHandler包括带fetch方法的对象、Hono 等第三方框架应用转换成中间件若其返回404状态码的Response则自动继续调用next()实现未命中则回退的链式挂载src/middleware.ts。test/middleware.test.ts 展示了将 Hono 应用挂载为 H3 中间件的用法。最佳实践小结优先组合式工具能写在处理器/defineHandler内部或使用basicAuth、bodyLimit等内置工具完成的逻辑尽量不写全局中间件只做横切关注点日志、鉴权、限流、响应包装、错误兜底是中间件的天然场景拦截要显式中间件只要返回非undefined/非next()结果的值就会立即短路响应务必确认这是预期行为善用匹配条件routemethodmatch三条件组合可以把中间件精确限定到需要的请求子集减少无关开销区分 hooks 与 middleware全局 Hooks 在主应用生效但子应用不继承需要覆盖挂载子应用的场景时改用中间件。【免费下载链接】h3⚡️ Minimal H(TTP) framework built for high performance and portability项目地址: https://gitcode.com/GitHub_Trending/h31/h3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价